Setup

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.2     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.3     ✔ tibble    3.2.1
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(here)
## here() starts at /Users/jaeyoungson/Documents/GitHub/network-navigation-replay
library(glmmTMB)
## Warning in checkMatrixPackageVersion(): Package version inconsistency detected.
## TMB was built with Matrix version 1.6.0
## Current Matrix version is 1.6.1
## Please re-install 'TMB' from source using install.packages('TMB', type = 'source') or ask CRAN for a binary version of 'TMB' matching CRAN's 'Matrix' package
## Warning in checkDepPackageVersion(dep_pkg = "TMB"): Package version inconsistency detected.
## glmmTMB was built with TMB version 1.9.4
## Current TMB version is 1.9.6
## Please re-install glmmTMB from source or restore original 'TMB' package (see '?reinstalling' for more information)
library(broom.mixed)

library(tidygraph)
## 
## Attaching package: 'tidygraph'
## 
## The following object is masked from 'package:stats':
## 
##     filter
library(ggraph)

library(kableExtra)
## 
## Attaching package: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(tictoc)
source(here("code", "utils", "ggplot_themes.R"))
source(here("code", "utils", "modeling_utils.R"))
source(here("code", "utils", "representation_utils.R"))
source(here("code", "utils", "unicode_greek.R"))

predict_glmmTMB <- function(make_predictions_for, model_object) {
  make_predictions_for %>%
    bind_cols(
      predict(
        object = model_object,
        newdata = .,
        re.form = NA, allow.new.levels = TRUE, se.fit = TRUE, type = "response"
      )
    )
}

check_significance <- function(tidy_df) {
  tidy_df %>%
    mutate(
      sig = case_when(
        p.value < 0.001 ~ "***",
        p.value < 0.01 ~ "**",
        p.value < 0.05 ~ "*",
        p.value < 0.1 ~ ".",
        TRUE ~ ""
      )
    )
}
# To control when to save figures
knitting <- knitr::is_html_output()

if (knitting) {
  if (!dir.exists(here("figures"))) {
    dir.create(here("figures"))
  }
}

Network visualizations

adjlist <- here("data", "clean-data", "adjlist_learned.csv") %>%
  read_csv(show_col_types = FALSE)

g <- adjlist %>%
  filter(from < to, edge == 1) %>%
  select(-edge) %>%
  tbl_graph(edges = ., directed = FALSE)

adjlist_reevaluated <- here("data", "clean-data", "adjlist_reevaluated.csv") %>%
  read_csv(show_col_types = FALSE)

g_reevaluated <- adjlist_reevaluated %>%
  filter(from < to, edge == 1) %>%
  select(-edge) %>%
  tbl_graph(edges = ., directed = FALSE)
plot_network_learned <- g %>%
  mutate(name = row_number()) %>%
  ggraph("stress") +
  theme_network() +
  geom_edge_link() +
  geom_node_label(aes(label = name))

plot_network_reevaluated <- g_reevaluated %>%
  mutate(name = row_number()) %>%
  ggraph("stress") +
  theme_network() +
  geom_edge_link() +
  geom_node_label(aes(label = name))

plot_network_learned
## Warning: Using the `size` aesthetic in this geom was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` in the `default_aes` field and elsewhere instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

plot_network_reevaluated

if (knitting) {
  ggsave(
    here("figures", str_c("network_learned", ".pdf")),
    plot = plot_network_learned,
    width = 4, height = 2,
    units = "in", dpi = 300
  )
  
  ggsave(
    here("figures", str_c("network_reevaluated", ".pdf")),
    plot = plot_network_reevaluated,
    width = 4, height = 2,
    units = "in", dpi = 300
  )
}

Can humans solve social navigation problems?

In the paper, we start by examining social navigation behaviors in a one-day session (Study 1), or in the first session of a two-day study (Studies 2-3). Across all three studies, the procedure is exactly identical; studies 2-3 are, in this part of the dataset, exact replications of study 1.

nav_study1 <- here("data", "clean-data", "study1_message_passing.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == FALSE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(
    study = "Study 1",
    measurement_id = str_c("D", measurement_id),
    shortest_path = factor(shortest_path_given_opts)
  ) %>%
  select(
    study, sub_id, measurement_id, shortest_path,
    startpoint_id, endpoint_id,
    opt1_id, opt2_id,
    correct_choice, sub_choice,
    correct, rt
  )

nav_study2 <- here("data", "clean-data", "study2_message_passing.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == FALSE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(
    study = "Study 2",
    measurement_id = case_when(
      network == "learned" ~ str_c("D", measurement_id),
      network == "reevaluated" ~ "D2b"
    ),
    shortest_path = factor(shortest_path_given_opts)
  ) %>%
  select(
    study, sub_id, measurement_id, shortest_path,
    startpoint_id, endpoint_id,
    opt1_id, opt2_id,
    correct_choice, sub_choice,
    correct, rt
  )

nav_study3 <- here("data", "clean-data", "study3_message_passing.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == FALSE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(
    study = "Study 3",
    measurement_id = case_when(
      network == "reevaluated" ~ "D2b",
      measurement_id == 1 ~ "D1",
      measurement_id == 2 ~ "D1b",
      measurement_id == 3 ~ "D2"
    ),
    shortest_path = factor(shortest_path_given_opts)
  ) %>%
  select(
    study, sub_id, measurement_id, shortest_path,
    startpoint_id, endpoint_id,
    opt1_id, opt2_id,
    correct_choice, sub_choice,
    correct, rt
  )

We’ll start off with some descriptive statistics of human behavior. To maximize statistical power, we will pool across studies whenever possible.

bind_rows(nav_study1, nav_study2, nav_study3) %>%
  filter(measurement_id == "D1") %>%
  group_by(measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  arrange(measurement_id, shortest_path) %>%
  pivot_wider(
    names_from = shortest_path, values_from = accuracy, names_prefix = "dist-"
  ) %>%
  kbl(
    caption = str_c(
      "<center>", "Descriptive: Navigation accuracy", "</center>"
    ),
    digits = 2
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
Descriptive: Navigation accuracy
measurement_id dist-2 dist-3 dist-4
D1 0.8 0.7 0.63

And now the inferential statistical tests. Note that we’re interested in knowing whether navigation accuracy differs from chance at each distance, so we’ll estimate the same statistical model multiple times, changing the reference category each time. Note that this only reparameterizes the model, such that the same variance is accounted for by different parameters; it does not change the total amount of variance accounted for.

nav_day1 <- bind_rows(nav_study1, nav_study2, nav_study3) %>%
  filter(measurement_id == "D1") %>%
  # Give every subject a distinct identifier
  mutate(sub_id = str_c(study, " s", sub_id))

stats_nav_day1_dist2 <- nav_day1 %>%
  mutate(shortest_path = fct_relevel(shortest_path, "2")) %>%
  glmmTMB(
    correct ~ shortest_path + (1 + shortest_path | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

stats_nav_day1_dist3 <- nav_day1 %>%
  mutate(shortest_path = fct_relevel(shortest_path, "3")) %>%
  glmmTMB(
    correct ~ shortest_path + (1 + shortest_path | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

stats_nav_day1_dist4 <- nav_day1 %>%
  mutate(shortest_path = fct_relevel(shortest_path, "4")) %>%
  glmmTMB(
    correct ~ shortest_path + (1 + shortest_path | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

map_dfr(
  .x = list(
    "dist-2" = stats_nav_day1_dist2,
    "dist-3" = stats_nav_day1_dist3,
    "dist-4" = stats_nav_day1_dist4
  ),
  .f = ~tidy(.x, conf.int = TRUE),
  .id = "ref_cat"
) %>%
  check_significance() %>%
  select(-c(ref_cat, effect, component)) %>%
  kbl(
    caption = str_c("<center>", "Navigation accuracy: Day 1", "</center>"),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive")) %>%
  pack_rows("Ref. Cat. dist-2", 1, 10) %>%
  pack_rows("Ref. Cat. dist-3", 11, 20) %>%
  pack_rows("Ref. Cat. dist-4", 21, 30)
Navigation accuracy: Day 1
group term estimate std.error statistic p.value conf.low conf.high sig
Ref. Cat. dist-2
NA (Intercept) 1.682 0.118 14.303 0 1.451 1.912 ***
NA shortest_path3 -0.622 0.066 -9.428 0 -0.751 -0.493 ***
NA shortest_path4 -1.026 0.081 -12.710 0 -1.184 -0.868 ***
sub_id sd__(Intercept) 1.041 NA NA NA 0.897 1.208
sub_id sd__shortest_path3 0.437 NA NA NA 0.320 0.596
sub_id sd__shortest_path4 0.748 NA NA NA 0.624 0.897
sub_id cor__(Intercept).shortest_path3 -0.392 NA NA NA -0.645 -0.008
sub_id cor__(Intercept).shortest_path4 -0.538 NA NA NA -0.884 0.179
sub_id cor__shortest_path3.shortest_path4 0.944 NA NA NA 0.236 0.976
study sd__(Intercept) 0.122 NA NA NA 0.019 0.765
Ref. Cat. dist-3
NA (Intercept) 1.059 0.114 9.335 0 0.837 1.282 ***
NA shortest_path2 0.622 0.066 9.428 0 0.493 0.751 ***
NA shortest_path4 -0.404 0.062 -6.549 0 -0.525 -0.283 ***
sub_id sd__(Intercept) 0.958 NA NA NA 0.807 1.137
sub_id sd__shortest_path2 0.437 NA NA NA 0.320 0.596
sub_id sd__shortest_path4 0.366 NA NA NA 0.243 0.549
sub_id cor__(Intercept).shortest_path2 -0.030 NA NA NA -0.398 0.351
sub_id cor__(Intercept).shortest_path4 -0.352 NA NA NA -0.364 0.248
sub_id cor__shortest_path2.shortest_path4 -0.737 NA NA NA -0.688 0.874
study sd__(Intercept) 0.122 NA NA NA 0.019 0.765
Ref. Cat. dist-4
NA (Intercept) 0.655 0.108 6.049 0 0.443 0.868 ***
NA shortest_path2 1.026 0.081 12.709 0 0.868 1.184 ***
NA shortest_path3 0.404 0.062 6.549 0 0.283 0.525 ***
sub_id sd__(Intercept) 0.897 NA NA NA 0.765 1.053
sub_id sd__shortest_path2 0.748 NA NA NA 0.624 0.897
sub_id sd__shortest_path3 0.366 NA NA NA 0.243 0.549
sub_id cor__(Intercept).shortest_path2 -0.209 NA NA NA -0.432 0.051
sub_id cor__(Intercept).shortest_path3 -0.031 NA NA NA -0.472 0.151
sub_id cor__shortest_path2.shortest_path3 0.919 NA NA NA -0.524 0.986
study sd__(Intercept) 0.122 NA NA NA 0.019 0.765

We’ll plot out the raw data, plus model predictions…

predict_nav_day1 <- expand_grid(
  measurement_id = "D1",
  shortest_path = factor(2:4),
  sub_id = NA, study = NA
) %>%
  predict_glmmTMB(stats_nav_day1_dist2)

plot_nav_day1 <- bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id == "D1") %>%
  group_by(sub_id, measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  ggplot(aes(x=shortest_path, y=accuracy)) +
  theme_custom() +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_dotplot(
    binwidth = 0.01,
    binaxis = "y", stackdir = "center",
    position = position_dodge(width = 0.75),
    dotsize = 1, alpha = 0.5, color = NA,
    show.legend = FALSE
  ) +
  geom_pointrange(
    aes(x = shortest_path, y = fit, ymin = fit - se.fit, ymax = fit + se.fit),
    data = predict_nav_day1, inherit.aes = FALSE, show.legend = FALSE,
    position = position_dodge(width = 0.15), linewidth = 1
  ) +
  geom_line(
    aes(x = shortest_path, y = fit, group = measurement_id),
    data = predict_nav_day1, inherit.aes = FALSE,
    position = position_dodge(width = 0.15), linewidth = 1
  ) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "Accuracy", labels = scales::percent, breaks = seq(0, 1, 0.1)
  ) +
  coord_cartesian(ylim = c(0.4, 1.1)) +
  theme(legend.position = "bottom") +
  ggtitle("Human social navigation")

plot_nav_day1

if (knitting) {
  ggsave(
    here("figures", str_c("navigation_day1", ".pdf")),
    plot = plot_nav_day1,
    width = 8/3, height = 3,
    units = "in", dpi = 300
  )
}

Computational models of social navigation

We’d like to have some mechanistic insights about how people solve navigation problems. To do this, we’ll look at two candidate models of navigation: breadth-first search (BFS) and the Successor Representation (SR).

Simulations

BFS simulation

sim_bfs <- here("data", "bfs-sims", "bfs_sims_learned.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == FALSE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(shortest_path = factor(shortest_path_given_opts)) %>%
  select(
    shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id,
    bfs_choice, bfs_correct_choice, bfs_n_visits_total
  )

In our implementation, we model the BFS agent having some “threshold” for searching through the network. This can be thought of as a “willingness to spend time/effort performing a search” threshold. Once that threshold is exceeded, it becomes increasingly likely that the agent gives up and chooses randomly.

To see what threshold values might be informative to look at, we’ll look at the average number of “searches” that an agent must perform to make a (non-random) decision.

sim_bfs %>%
  group_by(shortest_path) %>%
  summarise(avg_n_searches = mean(bfs_n_visits_total)) %>%
  kbl(
    caption = str_c("<center>", "Average searches in online-BFS", "</center>"),
    digits = 2
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
Average searches in online-BFS
shortest_path avg_n_searches
2 4.92
3 7.70
4 11.16

And now we’ll plot the model predictions…

bfs_avg_accuracy <- sim_bfs %>%
  group_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id) %>%
  summarise(
    bfs_accuracy = mean(bfs_correct_choice),
    bfs_visits = mean(bfs_n_visits_total),
    .groups = "drop"
  )

plot_sim_bfs <- bfs_avg_accuracy %>%
  expand_grid(search_threshold = seq(2, 12, 2)) %>%
  rowwise() %>%
  mutate(
    # Note: p(BFS) is 1-p(give up)
    p_bfs = softmax(
      option_values = c(search_threshold, bfs_visits),
      option_chosen = 1,
      temperature = 1
    )
  ) %>%
  ungroup() %>%
  # Weigh the model predictions according to their likelihood
  mutate(
    p_give_up = 1 - p_bfs,
    bfs_threshold_accuracy = (p_bfs * bfs_accuracy) + (p_give_up * (1/2))
  ) %>%
  # Format for plotting
  mutate(
    search_threshold = str_pad(search_threshold, width = 2, side = "left")
  ) %>%
  bind_rows(
    bfs_avg_accuracy %>%
      mutate(
        search_threshold = "Never gives up",
        bfs_threshold_accuracy = bfs_accuracy
      )
  ) %>%
  # Now plot
  ggplot(
    aes(
      x=shortest_path, y=bfs_threshold_accuracy,
      color=search_threshold, group=search_threshold
    )
  ) +
  theme_custom() +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  stat_summary(geom = "line", fun = mean, linewidth = 1) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(name = "Accuracy", labels = scales::percent) +
  scale_color_viridis_d(
    name = "Search threshold", option = "magma",
    begin = 0.1, end = 0.9, direction = -1
  ) +
  guides(color = guide_legend(byrow = TRUE, nrow = 1)) +
  coord_cartesian(ylim = c(0.5, 1)) +
  theme(
    panel.grid = element_blank(),
    legend.position = "bottom"
  ) +
  ggtitle("Simulated BFS navigation")

plot_sim_bfs

if (knitting) {
  ggsave(
    here("figures", str_c("simulated_bfs", ".pdf")),
    plot = plot_sim_bfs,
    width = 8/3, height = 3,
    units = "in", dpi = 300
  )
}

SR navigation simulation

triallist_nav_learned <- nav_study1 %>%
  filter(sub_id == 1) %>%
  select(
    startpoint_id, endpoint_id, opt1_id, opt2_id,
    correct_choice, shortest_path
  ) %>%
  arrange(startpoint_id, endpoint_id)

We’ll create some simulated learning observations.

set.seed(sum(utf8ToInt("Jenny and me was like peas and carrots")))

simulated_paired_associates <- adjlist %>%
  filter(edge == 1) %>%
  select(from, to) %>%
  expand_grid(set = 1:5000, .) %>%
  group_by(set) %>%
  slice_sample(prop = 1) %>%
  ungroup()

And now we’ll simulate an “asymptotic” SR and how it performs in the navigation task.

simulate_sr <- function(simulated_observations) {
  simulated_observations %>%
    expand_grid(gamma = seq(0.1, 0.9, 0.1)) %>%
    group_by(gamma) %>%
    nest() %>%
    mutate(
      sr = map(
        .x = data,
        .f = ~build_rep_sr(
          learning_data = .x, this_alpha = 0.1, this_gamma = gamma
        )
      )
    ) %>%
    unnest(sr) %>%
    ungroup() %>%
    select(-data)
}

join_sr <- function(navigation_triallist, simulated_sr) {
  navigation_triallist %>%
    left_join(
      simulated_sr %>%
        rename(opt1_sr = sr_value, opt1_id = from, endpoint_id = to)
    ) %>%
    left_join(
      simulated_sr %>%
        rename(opt2_sr = sr_value, opt2_id = from, endpoint_id = to)
    )
}

sim_sr_matrix_asymptotic <- simulated_paired_associates %>%
  simulate_sr()

sim_sr_behavior_asymptotic <- join_sr(
  triallist_nav_learned, sim_sr_matrix_asymptotic
) %>%
  # Feed values through softmax
  mutate(across(c(opt1_sr, opt2_sr), ~.x * 100)) %>%
  expand_grid(temperature = 1) %>%
  rowwise() %>%
  mutate(
    p_correct = softmax(
      option_values = c(opt1_sr, opt2_sr),
      option_chosen = if_else(correct_choice == opt1_id, 1, 2),
      temperature = temperature,
      use_inverse_temperature = TRUE
    )
  ) %>%
  ungroup()
## Joining with `by = join_by(endpoint_id, opt1_id)`
## Warning in left_join(., simulated_sr %>% rename(opt1_sr = sr_value, opt1_id = from, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 45 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this
##   warning.
## Joining with `by = join_by(endpoint_id, opt2_id, gamma)`
plot_sim_sr_asymptotic <- sim_sr_behavior_asymptotic %>%
  mutate(
    gamma = factor(gamma),
    temperature = str_pad(temperature, width = 2, side = "left"),
    temperature = str_c(unicode_greek["tau"], " = ", temperature)
  ) %>%
  group_by(gamma, temperature, shortest_path) %>%
  summarise(p_correct = mean(p_correct), .groups = "drop") %>%
  ggplot(aes(x=shortest_path, y=p_correct, color=gamma)) +
  theme_custom() +
  facet_grid(rows = vars(temperature)) +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_line(aes(color = gamma, group = gamma), linewidth = 0.8) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(name = "Accuracy", labels = scales::percent) +
  scale_color_viridis_d(
    name = str_c(unicode_greek["gamma"], " = "), option = "turbo", end = 0.9
  ) +
  guides(color = guide_legend(byrow = TRUE, nrow = 1)) +
  coord_cartesian(ylim = c(0.5, 1)) +
  theme(legend.position = "bottom") +
  ggtitle("Simulated SR navigation")

plot_sim_sr_asymptotic

if (knitting) {
  suppressWarnings(
    ggsave(
      here("figures", str_c("simulated_sr", ".pdf")),
      plot = plot_sim_sr_asymptotic,
      width = 8/3, height = 3,
      units = "in", dpi = 300,
    )
  )
  
  ggsave(
    here("figures", str_c("simulated_sr_cairo", ".pdf")),
    plot = plot_sim_sr_asymptotic +
      guides(color = guide_legend(byrow = TRUE, nrow = 2)),
    width = 8/3, height = 3,
    units = "in", dpi = 300,
    device = cairo_pdf
  )
}

Model comparison

To do model comparison, we’ll need to pull in the estimated parameters/likelihoods from the model-fitting.

clean_params_from_raw <- FALSE

if (clean_params_from_raw) {
  params <- here("data", "param-fits") %>%
    fs::dir_ls(regexp = "study[[:digit:]]_D[[:digit:]]b?") %>%
    map_dfr(
      .f = ~read_csv(.x, show_col_types = FALSE) %>%
        best_optim_run("dataframe"),
      .id = "filename"
    ) %>%
    mutate(
      study = str_extract(filename, "study[[:digit:]]"),
      study = str_remove(study, "study"),
      study = str_c("Study ", study),
      measurement_id = str_extract(filename, "_D[[:digit:]]b?"),
      measurement_id = str_remove(measurement_id, "_"),
      sub_id = str_extract(filename, "sub-[[:digit:]]+"),
      sub_id = str_remove(sub_id, "sub-"),
      sub_id = as.integer(sub_id),
      model = case_when(
        str_detect(filename, "_hybrid-") ~ "hybrid",
        str_detect(filename, "_sr-") ~ "sr",
        str_detect(filename, "_bfs-") ~ "bfs"
      )
    ) %>%
    select(
      study, sub_id, measurement_id,
      model,
      param_name, param_value = param_value_human_readable,
      neg_loglik = optim_value
    ) %>%
    arrange(study, sub_id, measurement_id, model)
  
  params %>%
    write_csv(here("data", "param-fits", "clean_param_fits.csv"))
} else {
  params <- here("data", "param-fits", "clean_param_fits.csv") %>%
    read_csv(show_col_types = FALSE)
}
source(here("code", "utils", "bayesian_model_selection.R"))

pxp_results <- params %>%
  select(study, sub_id, measurement_id, model, neg_loglik) %>%
  distinct() %>%
  mutate(log_lik = -neg_loglik) %>%
  select(-neg_loglik) %>%
  pivot_wider(names_from = model, values_from = log_lik) %>%
  select(-sub_id) %>%
  group_by(study, measurement_id) %>%
  nest() %>%
  mutate(
    test = map(
      .x = data,
      .f = ~bayesian_model_selection(.x)
    )
  ) %>%
  unnest(test) %>%
  ungroup() %>%
  select(-data)

pxp_results %>%
  ggplot(aes(x=measurement_id, y=pxp, color=model)) +
  theme_custom() +
  facet_wrap(~study, scales = "free_x") +
  geom_point(
    position = position_dodge(width = 0.75)
  )

pxp_results %>%
  select(study, measurement_id, model, pxp) %>%
  mutate(pxp = as.numeric(pxp)) %>%
  pivot_wider(
    names_from = model,
    values_from = pxp,
    names_prefix = "pxp_"
  ) %>%
  kbl(
    caption = str_c("<center>", "PXP results", "</center>"),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
PXP results
study measurement_id pxp_bfs pxp_hybrid pxp_sr
Study 1 D1 0 0.000 1.000
Study 2 D1 0 0.000 1.000
Study 2 D2 0 0.000 1.000
Study 3 D1 0 0.000 0.999
Study 3 D1b 0 0.000 1.000
Study 3 D2 0 0.006 0.994

Posterior predictive check

We’ll start by simulating the model’s predictions for each subject, given their estimated parameters.

ppc_bfs <- bind_rows(nav_study1, nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2")) %>%
  left_join(
    params %>%
      filter(model == "bfs") %>%
      pivot_wider(names_from = param_name, values_from = param_value) %>%
      select(study, sub_id, measurement_id, search_threshold, lapse_rate)
  ) %>%
  left_join(bfs_avg_accuracy) %>%
  mutate(
    p_sub_choice_bfs = if_else(
      sub_choice == correct_choice,
      bfs_accuracy,
      1 - bfs_accuracy
    )
  ) %>%
  # What's the probability of *completing* BFS-online all the way through?
  rowwise() %>%
  mutate(
    search_threshold = search_threshold,
    p_complete_bfs = softmax(
      option_values = c(search_threshold, bfs_visits),
      option_chosen = 1,
      temperature = 1
    )
  ) %>%
  ungroup() %>%
  # Weigh BFS predictions accordingly
  mutate(
    p_give_up = 1 - p_complete_bfs,
    predicted_correct = (p_complete_bfs * p_sub_choice_bfs) + (p_give_up * 1/2),
    ### Add lapse rate
    #   Dividing by 2 is because there are two options to choose from
    #   Therefore, when lapse rate = 1, this becomes chance = 1/2
    predicted_correct = predicted_correct * (1-lapse_rate) + (lapse_rate/2)
  ) %>%
  # Average over trials
  group_by(study, sub_id, measurement_id, shortest_path) %>%
  summarise(
    empirical = mean(correct),
    predicted = mean(predicted_correct),
    .groups = "drop"
  )
## Joining with `by = join_by(study, sub_id, measurement_id)`
## Joining with `by = join_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id)`
ppc_sr_matrix <- params %>%
  filter(model == "sr") %>%
  select(
    study, sub_id, measurement_id, name = param_name, value = param_value
  ) %>%
  pivot_wider() %>%
  group_by(study, sub_id, measurement_id) %>%
  nest() %>%
  mutate(
    sim_sr = map(
      .x = data,
      .f = ~build_rep_sr(
        learning_data = simulated_paired_associates %>% filter(set %in% 1:100),
        this_alpha = 0.1,
        this_gamma = .x$sr_gamma,
        bidirectional = TRUE
      )
    )
  ) %>%
  unnest(sim_sr) %>%
  unnest(data) %>%
  ungroup()

ppc_sr_behavior <- bind_rows(nav_study1, nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2")) %>%
  left_join(
    ppc_sr_matrix %>%
      rename(opt1_id = from, endpoint_id = to, opt1_sr = sr_value)
  ) %>%
  left_join(
    ppc_sr_matrix %>%
      rename(opt2_id = from, endpoint_id = to, opt2_sr = sr_value)
  ) %>%
  rowwise() %>%
  mutate(
    predicted_correct = softmax(
      option_values = c(opt1_sr, opt2_sr) * 100,
      option_chosen = if_else(correct_choice == opt1_id, 1, 2),
      temperature = softmax_temperature,
      use_inverse_temperature = TRUE,
      lapse_rate = lapse_rate
    )
  ) %>%
  ungroup() %>%
  # Fix trials when the softmax becomes undefined
  mutate(
    predicted_correct = case_when(
      is.nan(predicted_correct) & (sub_choice == correct_choice) ~ 1,
      is.nan(predicted_correct) & (sub_choice != correct_choice) ~ 0,
      TRUE ~ predicted_correct
    )
  ) %>%
  # Average over trials
  group_by(study, sub_id, measurement_id, shortest_path) %>%
  summarise(
    empirical = mean(correct),
    predicted = mean(predicted_correct),
    .groups = "drop"
  )
## Joining with `by = join_by(study, sub_id, measurement_id, endpoint_id, opt1_id)`
## Joining with `by = join_by(study, sub_id, measurement_id, endpoint_id, opt2_id, sr_gamma,
## softmax_temperature, lapse_rate)`

Now that we have both PPCs, we can plot them side-by-side.

plot_ppc_day1 <- ppc_bfs %>%
  rename(predicted_bfs = predicted) %>%
  left_join(ppc_sr_behavior %>% rename(predicted_sr = predicted)) %>%
  pivot_longer(
    c(empirical, predicted_bfs, predicted_sr),
    names_to = "agent", values_to = "accuracy"
  ) %>%
  filter(measurement_id == "D1") %>%
  ggplot(aes(x=shortest_path, y=accuracy)) +
  theme_custom() +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_point(
    aes(color = agent),
    alpha = 0.05,
    position = position_jitterdodge(
      jitter.width = 0.2, jitter.height = 0, dodge.width = 0.75, seed = 1
    ),
    show.legend = FALSE
  ) +
  stat_summary(
    aes(color = agent), geom = "crossbar", fun = mean,
    position = position_dodge(0.5)
  ) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "Accuracy", labels = scales::percent, breaks = seq(0, 1, 0.25)
  ) +
  scale_color_manual(
    name = NULL,
    values = c(
      "empirical"="#fd8d3c",
      "predicted_bfs"="#8c2d04",
      "predicted_sr"="#bd0026"
    ),
    labels = c("empirical"="Human", "predicted_bfs"="BFS", "predicted_sr"="SR")
  ) +
  theme(legend.position = "bottom") +
  ggtitle("Posterior predictive check")
## Joining with `by = join_by(study, sub_id, measurement_id, shortest_path,
## empirical)`
plot_ppc_day1

if (knitting) {
  ggsave(
    here("figures", str_c("ppc_day1", ".pdf")),
    plot = plot_ppc_day1,
    width = 8/2, height = 2.5,
    units = "in", dpi = 300
  )
}

Held-out trials

There were some trials where both Sources had equivalent shortest path distances from the Target. We would expect that BFS would be largely indifferent between the two Sources, but it is possible that humans and/or the SR would make other predictions.

nav_study1_ties <- here("data", "clean-data", "study1_message_passing.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == TRUE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(
    study = "Study 1",
    measurement_id = str_c("D", measurement_id),
    shortest_path = factor(shortest_path_given_opts)
  ) %>%
  select(
    study, sub_id, measurement_id, shortest_path,
    startpoint_id, endpoint_id,
    opt1_id, opt2_id,
    correct_choice, sub_choice,
    correct, rt
  ) %>%
  filter(measurement_id %in% c("D1", "D2"))

nav_study2_ties <- here("data", "clean-data", "study2_message_passing.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == TRUE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(
    study = "Study 2",
    measurement_id = case_when(
      network == "learned" ~ str_c("D", measurement_id),
      network == "reevaluated" ~ "D2b"
    ),
    shortest_path = factor(shortest_path_given_opts)
  ) %>%
  select(
    study, sub_id, measurement_id, shortest_path,
    startpoint_id, endpoint_id,
    opt1_id, opt2_id,
    correct_choice, sub_choice,
    correct, rt
  ) %>%
  filter(measurement_id %in% c("D1", "D2"))

nav_study3_ties <- here("data", "clean-data", "study3_message_passing.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == TRUE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(
    study = "Study 3",
    measurement_id = case_when(
      network == "reevaluated" ~ "D2b",
      measurement_id == 1 ~ "D1",
      measurement_id == 2 ~ "D1b",
      measurement_id == 3 ~ "D2"
    ),
    shortest_path = factor(shortest_path_given_opts)
  ) %>%
  select(
    study, sub_id, measurement_id, shortest_path,
    startpoint_id, endpoint_id,
    opt1_id, opt2_id,
    correct_choice, sub_choice,
    correct, rt
  ) %>%
  filter(measurement_id %in% c("D1", "D2"))
tie_item_analysis_humans <- bind_rows(
  nav_study1_ties, nav_study2_ties, nav_study3_ties
) %>%
  filter(measurement_id == "D1") %>%
  group_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id) %>%
  summarise(
    p_human_opt1 = mean(sub_choice == opt1_id),
    .groups = "drop"
  )
bfs_avg_accuracy_ties <- here("data", "bfs-sims", "bfs_sims_learned.csv") %>%
  read_csv(show_col_types = FALSE) %>%
  filter(
    two_correct_options == TRUE,
    shortest_path_given_opts == shortest_path_given_start_end
  ) %>%
  mutate(shortest_path = factor(shortest_path_given_opts)) %>%
  select(
    shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id,
    bfs_choice, bfs_correct_choice, bfs_n_visits_total
  ) %>%
  group_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id) %>%
  summarise(
    p_bfs_opt1 = mean(bfs_choice == opt1_id),
    bfs_visits = mean(bfs_n_visits_total),
    .groups = "drop"
  )

tie_item_analysis_bfs <- bind_rows(
  nav_study1_ties, nav_study2_ties, nav_study3_ties
) %>%
  filter(measurement_id == "D1") %>%
  left_join(
    params %>%
      filter(model == "bfs") %>%
      pivot_wider(names_from = param_name, values_from = param_value) %>%
      select(study, sub_id, measurement_id, search_threshold, lapse_rate)
  ) %>%
  left_join(bfs_avg_accuracy_ties) %>%
  mutate(
    p_sub_choice_bfs = if_else(
      sub_choice == opt1_id,
      p_bfs_opt1,
      1 - p_bfs_opt1
    )
  ) %>%
  select(-p_bfs_opt1) %>%
  # What's the probability of *completing* BFS-online all the way through?
  rowwise() %>%
  mutate(
    search_threshold = search_threshold,
    p_complete_bfs = softmax(
      option_values = c(search_threshold, bfs_visits),
      option_chosen = 1,
      temperature = 1
    )
  ) %>%
  ungroup() %>%
  # Weigh BFS predictions accordingly
  mutate(
    p_give_up = 1 - p_complete_bfs,
    p_bfs_opt1 = (p_complete_bfs * p_sub_choice_bfs) + (p_give_up * 1/2),
    ### Add lapse rate
    #   Dividing by 2 is because there are two options to choose from
    #   Therefore, when lapse rate = 1, this becomes chance = 1/2
    p_bfs_opt1 = p_bfs_opt1 * (1-lapse_rate) + (lapse_rate/2)
  ) %>%
  # Average over trials
  # group_by(shortest_path) %>%
  group_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id) %>%
  summarise(
    p_bfs_opt1 = mean(p_bfs_opt1),
    .groups = "drop"
  )
## Joining with `by = join_by(study, sub_id, measurement_id)`
## Joining with `by = join_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id)`
tie_item_analysis_sr <- bind_rows(
  nav_study1_ties, nav_study2_ties, nav_study3_ties
) %>%
  filter(measurement_id == "D1") %>%
  left_join(
    ppc_sr_matrix %>%
      select(
        study, sub_id, measurement_id,
        opt1_id = from,
        endpoint_id = to,
        opt1_sr = sr_value
      )
  ) %>%
  left_join(
    ppc_sr_matrix %>%
      select(
        study, sub_id, measurement_id,
        opt2_id = from,
        endpoint_id = to,
        opt2_sr = sr_value,
        sr_gamma, softmax_temperature, lapse_rate
      )
  ) %>%
  rowwise() %>%
  mutate(
    p_sr_opt1 = softmax(
      option_values = c(opt1_sr, opt2_sr) * 100,
      option_chosen = 1,
      temperature = softmax_temperature,
      use_inverse_temperature = TRUE,
      lapse_rate = lapse_rate
    )
  ) %>%
  group_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id) %>%
  summarise(
    p_sr_opt1 = mean(p_sr_opt1, na.rm = TRUE),
    .groups = "drop"
  )
## Joining with `by = join_by(study, sub_id, measurement_id, endpoint_id, opt1_id)`
## Joining with `by = join_by(study, sub_id, measurement_id, endpoint_id, opt2_id)`
plot_ties <- tie_item_analysis_humans %>%
  left_join(tie_item_analysis_bfs) %>%
  left_join(tie_item_analysis_sr) %>%
  mutate(item = row_number(), item = factor(item)) %>%
  pivot_longer(
    starts_with("p_"),
    names_to = "agent",
    values_to = "p_choose_opt1"
  ) %>%
  mutate(
    agent = str_remove_all(agent, "p_|_opt1"),
    agent = case_when(
      agent == "human" ~ "Human",
      agent == "sr" ~ "SR",
      agent == "bfs" ~ "BFS",
    ),
    agent = fct_relevel(agent, "Human", "SR")
  ) %>%
  ggplot(aes(x=shortest_path, y=p_choose_opt1)) +
  theme_custom() +
  facet_wrap(~agent) +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_point(alpha = 0.25) +
  stat_summary(geom = "crossbar", fun = mean, color = "red") +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "p(Choose Source A > B)",
    labels = scales::percent, breaks = seq(0, 1, 0.25)
  ) +
  theme(
    panel.grid = element_blank(),
    legend.position = "bottom"
  ) +
  ggtitle("Navigation problems with two correct answers")
## Joining with `by = join_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id)`
## Joining with `by = join_by(shortest_path, startpoint_id, endpoint_id, opt1_id, opt2_id)`
plot_ties

if (knitting) {
  ggsave(
    here("figures", str_c("navigation_heldout_day1", ".pdf")),
    plot = plot_ties,
    width = 8/2, height = 2.5,
    units = "in", dpi = 300
  )
}

Does navigation improve after rest?

First, some descriptives…

bind_rows(nav_study1, nav_study2, nav_study3) %>%
  group_by(measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  arrange(measurement_id, shortest_path) %>%
  pivot_wider(
    names_from = shortest_path, values_from = accuracy, names_prefix = "dist-"
  ) %>%
  kbl(
    caption = str_c(
      "<center>", "Descriptive: Navigation accuracy", "</center>"
    ),
    digits = 2
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
Descriptive: Navigation accuracy
measurement_id dist-2 dist-3 dist-4
D1 0.80 0.70 0.63
D1b 0.79 0.72 0.68
D2 0.82 0.75 0.71
D2b 0.81 0.72 0.62

Overnight rest

Now we want to see how navigation accuracy changes from day 1 to day 2. This pools across studies 2-3 (study 1 was only a one-day experiment).

nav_day1_to_day2 <- bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2")) %>%
  # Give every subject a distinct identifier
  mutate(sub_id = str_c(study, " s", sub_id))

stats_nav_day2_dist2 <- nav_day1_to_day2 %>%
  mutate(shortest_path = fct_relevel(shortest_path, "2")) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

stats_nav_day2_dist3 <- nav_day1_to_day2 %>%
  mutate(shortest_path = fct_relevel(shortest_path, "3")) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

stats_nav_day2_dist4 <- nav_day1_to_day2 %>%
  mutate(shortest_path = fct_relevel(shortest_path, "4")) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

map_dfr(
  .x = list(
    "dist-2" = stats_nav_day2_dist2,
    "dist-3" = stats_nav_day2_dist3,
    "dist-4" = stats_nav_day2_dist4
  ),
  .f = ~tidy(.x, conf.int = TRUE),
  .id = "ref_cat"
) %>%
  check_significance() %>%
  select(-c(ref_cat, effect, component)) %>%
  kbl(
    caption = str_c(
      "<center>", "Navigation accuracy: Day 1 to Day 2", "</center>"
    ),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive")) %>%
  pack_rows("Ref. Cat. dist-2", 1, 17) %>%
  pack_rows("Ref. Cat. dist-3", 18, 34) %>%
  pack_rows("Ref. Cat. dist-4", 35, 51)
Navigation accuracy: Day 1 to Day 2
group term estimate std.error statistic p.value conf.low conf.high sig
Ref. Cat. dist-2
NA (Intercept) 1.765 0.114 15.476 0.000 1.541 1.988 ***
NA shortest_path3 -0.452 0.083 -5.452 0.000 -0.615 -0.290 ***
NA shortest_path4 -0.959 0.091 -10.554 0.000 -1.137 -0.781 ***
NA measurement_idD2 0.234 0.080 2.906 0.004 0.076 0.391 **
NA shortest_path3:measurement_idD2 0.030 0.087 0.351 0.725 -0.139 0.200
NA shortest_path4:measurement_idD2 0.196 0.083 2.356 0.018 0.033 0.359
sub_id sd__(Intercept) 1.024 NA NA NA 0.858 1.223
sub_id sd__shortest_path3 0.461 NA NA NA 0.334 0.635
sub_id sd__shortest_path4 0.634 NA NA NA 0.512 0.785
sub_id sd__measurement_idD2 0.483 NA NA NA 0.370 0.631
sub_id cor__(Intercept).shortest_path3 0.182 NA NA NA -0.175 0.481
sub_id cor__(Intercept).shortest_path4 -0.250 NA NA NA -0.799 0.057
sub_id cor__(Intercept).measurement_idD2 0.451 NA NA NA -0.037 0.608
sub_id cor__shortest_path3.shortest_path4 0.808 NA NA NA 0.311 0.873
sub_id cor__shortest_path3.measurement_idD2 0.466 NA NA NA -0.116 0.837
sub_id cor__shortest_path4.measurement_idD2 0.007 NA NA NA -0.520 0.712
study sd__(Intercept) 0.000 NA NA NA 0.000 Inf
Ref. Cat. dist-3
NA (Intercept) 1.312 0.134 9.810 0.000 1.050 1.574 ***
NA shortest_path2 0.452 0.083 5.452 0.000 0.290 0.615 ***
NA shortest_path4 -0.507 0.079 -6.411 0.000 -0.662 -0.352 ***
NA measurement_idD2 0.264 0.088 2.989 0.003 0.091 0.437 **
NA shortest_path2:measurement_idD2 -0.030 0.087 -0.351 0.725 -0.200 0.139
NA shortest_path4:measurement_idD2 0.166 0.090 1.833 0.067 -0.011 0.343 .
sub_id sd__(Intercept) 1.197 NA NA NA 0.997 1.437
sub_id sd__shortest_path2 0.461 NA NA NA 0.334 0.635
sub_id sd__shortest_path4 0.377 NA NA NA 0.266 0.536
sub_id sd__measurement_idD2 0.483 NA NA NA 0.370 0.631
sub_id cor__(Intercept).shortest_path2 -0.541 NA NA NA -0.743 -0.174
sub_id cor__(Intercept).shortest_path4 -0.497 NA NA NA -0.605 0.049
sub_id cor__(Intercept).measurement_idD2 0.565 NA NA NA -0.045 0.827
sub_id cor__shortest_path2.shortest_path4 -0.137 NA NA NA -0.002 0.234
sub_id cor__shortest_path2.measurement_idD2 -0.466 NA NA NA -0.209 0.025
sub_id cor__shortest_path4.measurement_idD2 -0.557 NA NA NA -0.093 0.401
study sd__(Intercept) 0.000 NA NA NA 0.000 Inf
Ref. Cat. dist-4
NA (Intercept) 0.805 0.118 6.827 0.000 0.574 1.037 ***
NA shortest_path2 0.959 0.091 10.554 0.000 0.781 1.137 ***
NA shortest_path3 0.507 0.079 6.411 0.000 0.352 0.662 ***
NA measurement_idD2 0.430 0.083 5.174 0.000 0.267 0.592 ***
NA shortest_path2:measurement_idD2 -0.196 0.083 -2.356 0.018 -0.359 -0.033
NA shortest_path3:measurement_idD2 -0.166 0.090 -1.834 0.067 -0.343 0.011 .
sub_id sd__(Intercept) 1.061 NA NA NA 0.883 1.276
sub_id sd__shortest_path2 0.634 NA NA NA 0.512 0.785
sub_id sd__shortest_path3 0.377 NA NA NA 0.265 0.536
sub_id sd__measurement_idD2 0.483 NA NA NA 0.370 0.631
sub_id cor__(Intercept).shortest_path2 -0.356 NA NA NA -0.587 -0.038
sub_id cor__(Intercept).shortest_path3 0.205 NA NA NA -0.465 0.400
sub_id cor__(Intercept).measurement_idD2 0.439 NA NA NA -0.096 0.450
sub_id cor__shortest_path2.shortest_path3 0.695 NA NA NA 0.448 0.848
sub_id cor__shortest_path2.measurement_idD2 -0.007 NA NA NA -0.118 0.214
sub_id cor__shortest_path3.measurement_idD2 0.557 NA NA NA -0.497 0.628
study sd__(Intercept) 0.000 NA NA NA 0.000 Inf
predict_nav_day1_to_day2 <- expand_grid(
  measurement_id = c("D1", "D2"),
  shortest_path = factor(2:4),
  sub_id = NA, study = NA
) %>%
  predict_glmmTMB(stats_nav_day2_dist2)

plot_nav_day1_to_day2 <- nav_day1_to_day2 %>%
  group_by(sub_id, measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  ggplot(aes(x=shortest_path, y=accuracy, color=measurement_id)) +
  theme_custom() +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_dotplot(
    aes(fill = measurement_id),
    binwidth = 0.01,
    binaxis = "y", stackdir = "center",
    position = position_dodge(width = 0.75),
    dotsize = 1, alpha = 0.5, color = NA,
    show.legend = FALSE
  ) +
  geom_pointrange(
    aes(
      x = shortest_path, y = fit,
      ymin = fit - se.fit, ymax = fit + se.fit,
      color = measurement_id
    ),
    data = predict_nav_day1_to_day2, inherit.aes = FALSE, show.legend = FALSE,
    position = position_dodge(width = 0.15), linewidth = 1
  ) +
  geom_line(
    aes(
      x = shortest_path, y = fit,
      group = measurement_id, color = measurement_id
    ),
    data = predict_nav_day1_to_day2, inherit.aes = FALSE,
    position = position_dodge(width = 0.15), linewidth = 1
  ) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "Accuracy", labels = scales::percent, breaks = seq(0, 1, 0.25)
  ) +
  scale_color_manual(
    name = "Measurement",
    values = c("D1"="#fa9fb5", "D2"="#7a0177"),
    labels = c("D1"="Before overnight rest", "D2"="After overnight rest")
  ) +
  scale_fill_manual(values = c("D1"="#fa9fb5", "D2"="#7a0177")) +
  coord_cartesian(ylim = c(0.3, 1.1)) +
  theme(legend.position = "bottom") +
  ggtitle("Navigation after overnight rest")

plot_nav_day1_to_day2

if (knitting) {
  ggsave(
    here("figures", str_c("navigation_day1_to_day2", ".pdf")),
    plot = plot_nav_day1_to_day2,
    width = 4, height = 3,
    units = "in", dpi = 300
  )
}

Awake rest

Is a brief period of awake rest sufficient for improving navigation accuracy?

stats_nav_awake_dist2 <- nav_study3 %>%
  filter(measurement_id %in% c("D1", "D1b")) %>%
  mutate(shortest_path = fct_relevel(shortest_path, "2")) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id),
    family = binomial,
    data = .
  )

stats_nav_awake_dist3 <- nav_study3 %>%
  filter(measurement_id %in% c("D1", "D1b")) %>%
  mutate(shortest_path = fct_relevel(shortest_path, "3")) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id),
    family = binomial,
    data = .
  )

stats_nav_awake_dist4 <- nav_study3 %>%
  filter(measurement_id %in% c("D1", "D1b")) %>%
  mutate(shortest_path = fct_relevel(shortest_path, "4")) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id),
    family = binomial,
    data = .
  )

map_dfr(
  .x = list(
    "dist-2" = stats_nav_awake_dist2,
    "dist-3" = stats_nav_awake_dist3,
    "dist-4" = stats_nav_awake_dist4
  ),
  .f = ~tidy(.x, conf.int = TRUE),
  .id = "ref_cat"
) %>%
  check_significance() %>%
  select(-c(ref_cat, effect, component)) %>%
  kbl(
    caption = str_c("<center>", "Navigation accuracy: Awake Rest", "</center>"),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive")) %>%
  pack_rows("Ref. Cat. dist-2", 1, 16) %>%
  pack_rows("Ref. Cat. dist-3", 17, 32) %>%
  pack_rows("Ref. Cat. dist-4", 33, 48)
Navigation accuracy: Awake Rest
group term estimate std.error statistic p.value conf.low conf.high sig
Ref. Cat. dist-2
NA (Intercept) 1.840 0.193 9.542 0.000 1.462 2.218 ***
NA shortest_path3 -0.424 0.129 -3.290 0.001 -0.677 -0.171 **
NA shortest_path4 -0.936 0.134 -6.972 0.000 -1.199 -0.673 ***
NA measurement_idD1b -0.058 0.122 -0.475 0.635 -0.296 0.181
NA shortest_path3:measurement_idD1b 0.022 0.123 0.175 0.861 -0.220 0.263
NA shortest_path4:measurement_idD1b 0.244 0.119 2.060 0.039 0.012 0.477
sub_id sd__(Intercept) 1.216 NA NA NA 0.947 1.561
sub_id sd__shortest_path3 0.518 NA NA NA 0.352 0.764
sub_id sd__shortest_path4 0.632 NA NA NA 0.465 0.859
sub_id sd__measurement_idD1b 0.544 NA NA NA 0.395 0.751
sub_id cor__(Intercept).shortest_path3 -0.019 NA NA NA -0.462 0.435
sub_id cor__(Intercept).shortest_path4 -0.262 NA NA NA -0.840 0.127
sub_id cor__(Intercept).measurement_idD1b 0.045 NA NA NA -0.264 0.378
sub_id cor__shortest_path3.shortest_path4 0.845 NA NA NA 0.444 0.913
sub_id cor__shortest_path3.measurement_idD1b 0.191 NA NA NA -0.062 0.647
sub_id cor__shortest_path4.measurement_idD1b -0.024 NA NA NA -0.184 0.641
Ref. Cat. dist-3
NA (Intercept) 1.416 0.211 6.714 0.000 1.003 1.829 ***
NA shortest_path2 0.424 0.129 3.290 0.001 0.171 0.677 **
NA shortest_path4 -0.512 0.116 -4.427 0.000 -0.738 -0.285 ***
NA measurement_idD1b -0.036 0.133 -0.273 0.785 -0.296 0.224
NA shortest_path2:measurement_idD1b -0.022 0.123 -0.175 0.861 -0.263 0.220
NA shortest_path4:measurement_idD1b 0.223 0.129 1.722 0.085 -0.031 0.476 .
sub_id sd__(Intercept) 1.313 NA NA NA 1.010 1.705
sub_id sd__shortest_path2 0.518 NA NA NA 0.352 0.764
sub_id sd__shortest_path4 0.338 NA NA NA 0.190 0.603
sub_id sd__measurement_idD1b 0.544 NA NA NA 0.395 0.751
sub_id cor__(Intercept).shortest_path2 -0.377 NA NA NA -0.704 0.175
sub_id cor__(Intercept).shortest_path4 -0.408 NA NA NA -0.681 0.342
sub_id cor__(Intercept).measurement_idD1b 0.117 NA NA NA -0.224 0.497
sub_id cor__shortest_path2.shortest_path4 -0.046 NA NA NA 0.106 0.502
sub_id cor__shortest_path2.measurement_idD1b -0.191 NA NA NA -0.124 0.355
sub_id cor__shortest_path4.measurement_idD1b -0.337 NA NA NA 0.009 0.560
Ref. Cat. dist-4
NA (Intercept) 0.904 0.193 4.684 0.000 0.526 1.283 ***
NA shortest_path2 0.936 0.134 6.971 0.000 0.673 1.199 ***
NA shortest_path3 0.512 0.116 4.427 0.000 0.285 0.738 ***
NA measurement_idD1b 0.187 0.126 1.476 0.140 -0.061 0.434
NA shortest_path2:measurement_idD1b -0.244 0.119 -2.060 0.039 -0.477 -0.012
NA shortest_path3:measurement_idD1b -0.223 0.129 -1.722 0.085 -0.476 0.031 .
sub_id sd__(Intercept) 1.214 NA NA NA 0.935 1.577
sub_id sd__shortest_path2 0.632 NA NA NA 0.465 0.859
sub_id sd__shortest_path3 0.338 NA NA NA 0.190 0.603
sub_id sd__measurement_idD1b 0.544 NA NA NA 0.395 0.751
sub_id cor__(Intercept).shortest_path2 -0.258 NA NA NA -0.602 0.216
sub_id cor__(Intercept).shortest_path3 0.163 NA NA NA -0.621 0.521
sub_id cor__(Intercept).measurement_idD1b 0.033 NA NA NA -0.386 0.307
sub_id cor__shortest_path2.shortest_path3 0.573 NA NA NA 0.343 0.835
sub_id cor__shortest_path2.measurement_idD1b 0.024 NA NA NA -0.041 0.338
sub_id cor__shortest_path3.measurement_idD1b 0.337 NA NA NA -0.003 0.674
predict_nav_awake <- expand_grid(
  measurement_id = c("D1", "D1b"),
  shortest_path = factor(2:4),
  sub_id = NA, study = NA
) %>%
  predict_glmmTMB(stats_nav_awake_dist2)

plot_nav_awake <- nav_study3 %>%
  filter(measurement_id %in% c("D1", "D1b")) %>%
  group_by(sub_id, measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  ggplot(aes(x=shortest_path, y=accuracy, color=measurement_id)) +
  theme_custom() +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_dotplot(
    aes(fill = measurement_id),
    binwidth = 0.01,
    binaxis = "y", stackdir = "center",
    position = position_dodge(width = 0.75),
    dotsize = 1, alpha = 0.5, color = NA,
    show.legend = FALSE
  ) +
  geom_pointrange(
    aes(
      x = shortest_path, y = fit,
      ymin = fit - se.fit, ymax = fit + se.fit,
      color = measurement_id
    ),
    data = predict_nav_awake, inherit.aes = FALSE, show.legend = FALSE,
    position = position_dodge(width = 0.15), linewidth = 1
  ) +
  geom_line(
    aes(
      x = shortest_path, y = fit,
      group = measurement_id, color = measurement_id
    ),
    data = predict_nav_awake, inherit.aes = FALSE,
    position = position_dodge(width = 0.15), linewidth = 1
  ) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "Accuracy", labels = scales::percent, breaks = seq(0, 1, 0.25)
  ) +
  scale_color_manual(
    name = "Measurement",
    values = c("D1"="#fa9fb5", "D1b"="#2c7fb8"),
    labels = c("D1"="Before overnight rest", "D1b"="After awake rest")
  ) +
  scale_fill_manual(values = c("D1"="#fa9fb5", "D1b"="#2c7fb8")) +
  coord_cartesian(ylim = c(0.3, 1.1)) +
  theme(legend.position = "bottom") +
  ggtitle("Navigation after awake rest")

plot_nav_awake

if (knitting) {
  ggsave(
    here("figures", str_c("navigation_awake_rest", ".pdf")),
    plot = plot_nav_awake,
    width = 4, height = 3,
    units = "in", dpi = 300
  )
}

SR replay simulation

Before starting to do any simulation, we first want to know how much replay an agent can fit into different periods of time. Based on past research measuring neural replay events, we’ll assume that it takes about 50ms for the brain to replay a single item from a sequence. This will let us calculate the total number of items that could be replayed in each epoch. To simplify the process of actually running the simulation, we’ll convert this quantity into the number of “sets” that can be replayed, where a single “set” consists of the total number of relationships in the network (i.e., the number of undirected edges = 17, meaning that each set consists of 34 observations because each set contains observations of both A->B and B->A).

tibble(minutes_for_replay = c(1, 5, 15, 30, 60, 120)) %>%
  mutate(
    n_items = minutes_for_replay / (0.05 / 60),
    n_sets = n_items / 34
  ) %>%
  kbl(
    caption = str_c("<center>", "SR replay time", "</center>"),
    digits = 2
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
SR replay time
minutes_for_replay n_items n_sets
1 1200 35.29
5 6000 176.47
15 18000 529.41
30 36000 1058.82
60 72000 2117.65
120 144000 4235.29
sim_replay_0_min <- simulated_paired_associates %>%
  filter(set <= 6) %>%
  simulate_sr() %>%
  mutate(replay_time = 0)

sim_replay_1_min <- simulated_paired_associates %>%
  filter(set <= 35 + 6) %>%
  simulate_sr() %>%
  mutate(replay_time = 1)

sim_replay_5_min <- simulated_paired_associates %>%
  filter(set <= 176 + 6) %>%
  simulate_sr() %>%
  mutate(replay_time = 5)

sim_replay_60_min <- simulated_paired_associates %>%
  filter(set <= 2117 + 6) %>%
  simulate_sr() %>%
  mutate(replay_time = 60)
plot_sim_sr_replay <- bind_rows(
  join_sr(triallist_nav_learned, sim_replay_0_min),
  join_sr(triallist_nav_learned, sim_replay_1_min),
  join_sr(triallist_nav_learned, sim_replay_5_min),
  join_sr(triallist_nav_learned, sim_replay_60_min)
) %>%
  # Feed values through softmax
  mutate(across(c(opt1_sr, opt2_sr), ~.x * 100)) %>%
  expand_grid(temperature = 1) %>%
  rowwise() %>%
  mutate(
    p_correct = softmax(
      option_values = c(opt1_sr, opt2_sr),
      option_chosen = if_else(correct_choice == opt1_id, 1, 2),
      temperature = temperature,
      use_inverse_temperature = TRUE
    )
  ) %>%
  ungroup() %>%
  # For plotting
  mutate(
    gamma = factor(gamma),
    temperature = str_pad(temperature, width = 2, side = "left"),
    temperature = str_c(unicode_greek["tau"], " = ", temperature),
    replay_time = str_pad(replay_time, width = 2, side = "left"),
    replay_time = case_when(
      str_detect(replay_time, "1$") ~ str_c(replay_time, " minute of replay"),
      replay_time == " 0" ~ "No replay",
      TRUE ~ str_c(replay_time, " minutes of replay")
    ),
    replay_time = fct_relevel(replay_time, "No replay")
  ) %>%
  group_by(replay_time, gamma, temperature, shortest_path) %>%
  summarise(p_correct = mean(p_correct), .groups = "drop") %>%
  # Plot
  ggplot(aes(x=shortest_path, y=p_correct, color=gamma)) +
  theme_custom() +
  facet_grid(cols = vars(replay_time), rows = vars(temperature)) +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_line(aes(color = gamma, group = gamma), linewidth = 0.8) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(name = "Accuracy", labels = scales::percent) +
  scale_color_viridis_d(
    name = str_c(unicode_greek["gamma"], " = "), option = "turbo", end = 0.9
  ) +
  guides(color = guide_legend(byrow = TRUE, nrow = 1)) +
  coord_cartesian(ylim = c(0.5, 1)) +
  theme(legend.position = "bottom") +
  ggtitle("Simulated effects of SR replay on navigation")
## Joining with `by = join_by(endpoint_id, opt1_id)`
## Warning in left_join(., simulated_sr %>% rename(opt1_sr = sr_value, opt1_id = from, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 45 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this
##   warning.
## Joining with `by = join_by(endpoint_id, opt2_id, gamma, replay_time)`
## Joining with `by = join_by(endpoint_id, opt1_id)`
## Warning in left_join(., simulated_sr %>% rename(opt1_sr = sr_value, opt1_id = from, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 45 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this
##   warning.
## Joining with `by = join_by(endpoint_id, opt2_id, gamma, replay_time)`
## Joining with `by = join_by(endpoint_id, opt1_id)`
## Warning in left_join(., simulated_sr %>% rename(opt1_sr = sr_value, opt1_id = from, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 45 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this
##   warning.
## Joining with `by = join_by(endpoint_id, opt2_id, gamma, replay_time)`
## Joining with `by = join_by(endpoint_id, opt1_id)`
## Warning in left_join(., simulated_sr %>% rename(opt1_sr = sr_value, opt1_id = from, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 45 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this
##   warning.
## Joining with `by = join_by(endpoint_id, opt2_id, gamma, replay_time)`
plot_sim_sr_replay

if (knitting) {
  ggsave(
    here("figures", str_c("simulated_replay", ".pdf")),
    plot = plot_sim_sr_replay,
    width = 7, height = 2.5,
    units = "in", dpi = 300,
    device = cairo_pdf
  )
}

PPC before/after rest

plot_ppc_day1_to_day2 <- ppc_sr_behavior %>%
  filter(study %in% c("Study 2", "Study 3")) %>%
  mutate(
    measurement_id = case_when(
      measurement_id == "D1" ~ "Before rest",
      measurement_id == "D2" ~ "After rest"
    ),
    measurement_id = fct_relevel(measurement_id, "Before rest")
  ) %>%
  pivot_longer(c(empirical, predicted)) %>%
  ggplot(aes(x=shortest_path, y=value)) +
  theme_custom() +
  facet_wrap(~measurement_id) +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_point(
    aes(color = name),
    alpha = 0.05,
    position = position_jitterdodge(
      jitter.width = 0.2, jitter.height = 0, dodge.width = 0.5, seed = 1
    ),
    show.legend = FALSE
  ) +
  stat_summary(
    aes(color = name), geom = "crossbar", fun = mean,
    position = position_dodge(0.5)
  ) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "Accuracy", labels = scales::percent, breaks = seq(0, 1, 0.25)
  ) +
  scale_color_manual(
    name = NULL,
    values = c("empirical"="#fd8d3c", "predicted"="#bd0026"),
    labels = c("empirical"="Human", "predicted"="SR")
  ) +
  theme(legend.position = "bottom") +
  ggtitle("Posterior predictive check")

plot_ppc_day1_to_day2

if (knitting) {
  ggsave(
    here("figures", str_c("ppc_day2", ".pdf")),
    plot = plot_ppc_day1_to_day2,
    width = 8/3, height = 2.5,
    units = "in", dpi = 300
  )
}

Relating model parameters to navigation

Does estimated gamma significantly increase after overnight rest?

params %>%
  filter(
    study %in% c("Study 2", "Study 3"),
    measurement_id %in% c("D1", "D2"),
    model == "sr",
    param_name == "sr_gamma"
  ) %>%
  select(study, sub_id, measurement_id, sr_gamma = param_value) %>%
  group_by(measurement_id) %>%
  summarise(median_gamma = median(sr_gamma)) %>%
  kbl(
    caption = str_c(
      "<center>", "Median SR gamma before/after overnight rest", "</center>"
    ),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
Median SR gamma before/after overnight rest
measurement_id median_gamma
D1 0.512
D2 0.659
params %>%
  filter(
    study %in% c("Study 2", "Study 3"),
    measurement_id %in% c("D1", "D2"),
    model == "sr",
    param_name == "sr_gamma"
  ) %>%
  select(study, sub_id, measurement_id, sr_gamma = param_value) %>%
  pivot_wider(names_from = measurement_id, values_from = sr_gamma) %>%
  with(
    wilcox.test(
      D2, D1, alternative = "greater", paired = TRUE, conf.int = TRUE
    )
  ) %>%
  tidy() %>%
  kbl(
    caption = str_c(
      "<center>", "Increase in SR gamma after overnight rest", "</center>"
    ),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
Increase in SR gamma after overnight rest
estimate statistic p.value conf.low conf.high method alternative
0.064 2876 0.023 0.009 Inf Wilcoxon signed rank test with continuity correction greater
plot_gamma_change <- params %>%
  filter(
    study %in% c("Study 2", "Study 3"),
    measurement_id %in% c("D1", "D2"),
    model == "sr",
    param_name == "sr_gamma"
  ) %>%
  select(study, sub_id, measurement_id, sr_gamma = param_value) %>%
  ggplot(aes(x=measurement_id, y=sr_gamma, color=measurement_id)) +
  theme_custom() +
  geom_point(
    position = position_jitterdodge(
      jitter.width = 0.25, jitter.height = 0, dodge.width = 0.5, seed = 1
    ),
    alpha = 0.25,
    show.legend = FALSE
  ) +
  stat_summary(
    geom = "crossbar", fun = median, position = "dodge", show.legend = FALSE
  ) +
  scale_x_discrete(
    name = "Measurement",
    labels = c("D1"="Before rest", "D2"="After rest")
  ) +
  scale_y_continuous(
    name = str_c("Estimated ", unicode_greek["gamma"]),
    breaks = seq(0, 1, 0.25)
  ) +
  scale_color_manual(
    name = "Measurement",
    values = c("D1"="#fa9fb5", "D2"="#7a0177"),
    labels = c("D1"="Before rest", "D2"="After rest")
  ) +
  coord_cartesian(ylim = c(0, 1.1)) +
  theme(legend.position = "bottom") +
  ggtitle(
    str_c(
      unicode_greek["Delta"], unicode_greek["gamma"], " after overnight rest"
    )
  )

plot_gamma_change

if (knitting) {
  ggsave(
    here("figures", str_c("gamma_change", ".pdf")),
    plot = plot_gamma_change,
    width = 8/3, height = 2.5,
    units = "in", dpi = 300,
    device = cairo_pdf
  )
}

Are changes in estimated gamma related to changes in navigation behaviors?

bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2")) %>%
  group_by(study, sub_id, measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  pivot_wider(names_from = measurement_id, values_from = accuracy) %>%
  mutate(delta_accuracy = D2 - D1) %>%
  select(-c(D1, D2)) %>%
  # 
  left_join(
    params %>%
      filter(
        study %in% c("Study 2", "Study 3"),
        measurement_id %in% c("D1", "D2"),
        model == "sr",
        param_name == "sr_gamma"
      ) %>%
      select(study, sub_id, measurement_id, sr_gamma = param_value) %>%
      pivot_wider(names_from = measurement_id, values_from = sr_gamma) %>%
      mutate(delta_sr = D2 - D1) %>%
      select(-c(D1, D2)),
    by = c("study", "sub_id")
  ) %>%
  group_by(shortest_path) %>%
  nest() %>%
  mutate(
    test = map(
      .x = data,
      .f = ~with(
        .x,
        cor.test(
          delta_accuracy, delta_sr,
          method = "spearman", exact = FALSE, alternative = "greater"
        )
      ) %>% tidy()
    )
  ) %>%
  unnest(test) %>%
  ungroup() %>%
  select(-data) %>%
  kbl(
    caption = str_c(
      "<center>", "∆ Accuracy ~ ∆ SR gamma (after overnight rest)", "</center>"
    ),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive"))
∆ Accuracy ~ ∆ SR gamma (after overnight rest)
shortest_path estimate statistic p.value method alternative
2 -0.180 173977.52 0.960 Spearman’s rank correlation rho greater
3 0.205 117163.09 0.022 Spearman’s rank correlation rho greater
4 0.453 80620.94 0.000 Spearman’s rank correlation rho greater
plot_gamma_accuracy_change <- bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2")) %>%
  group_by(study, sub_id, measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  pivot_wider(names_from = measurement_id, values_from = accuracy) %>%
  mutate(delta_accuracy = D2 - D1) %>%
  select(-c(D1, D2)) %>%
  # 
  left_join(
    params %>%
      filter(
        study %in% c("Study 2", "Study 3"),
        measurement_id %in% c("D1", "D2"),
        model == "sr",
        param_name == "sr_gamma"
      ) %>%
      select(study, sub_id, measurement_id, sr_gamma = param_value) %>%
      pivot_wider(names_from = measurement_id, values_from = sr_gamma) %>%
      mutate(delta_sr = D2 - D1) %>%
      select(-c(D1, D2)),
    by = c("study", "sub_id")
  ) %>%
  ggplot(aes(x=delta_sr, y=delta_accuracy, color=shortest_path)) +
  theme_custom() +
  geom_hline(yintercept = 0, linetype = "dashed") +
  geom_vline(xintercept = 0, linetype = "dashed") +
  geom_point(alpha = 0.25, show.legend = FALSE) +
  geom_smooth(method = "lm", se = FALSE, linewidth = 1.5) +
  scale_x_continuous(name = str_c("Change in ", unicode_greek["gamma"])) +
  scale_y_continuous(name = "Change in accuracy") +
  scale_color_manual(
    name = "Shortest path distance",
    values = c("#88CCEE", "#CC6677", "#DDCC77")
  ) +
  coord_cartesian(xlim = c(-1, 1.1)) +
  theme(legend.position = "bottom") +
  ggtitle(
    str_c(
      unicode_greek["Delta"], "Navigation ~ ",
      unicode_greek["Delta"], unicode_greek["gamma"]
    )
  )

plot_gamma_accuracy_change
## `geom_smooth()` using formula = 'y ~ x'

if (knitting) {
  suppressWarnings(
    ggsave(
      here("figures", str_c("gamma_accuracy_change", ".pdf")),
      plot = plot_gamma_accuracy_change,
      width = 8/3, height = 2.5,
      units = "in", dpi = 300
    )
  )
  
  ggsave(
    here("figures", str_c("gamma_accuracy_change_cairo", ".pdf")),
    plot = plot_gamma_accuracy_change,
    width = 8/3, height = 2.5,
    units = "in", dpi = 300,
    device = cairo_pdf
  )
}
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'

Evidence of cached representation

Here, we’re trying to get a sense for what is being cached, and trying to see if there’s evidence that caching (as opposed to model-based planning) is the primary driver of navigation improvement after overnight rest.

Simulation visualization

butterfly_layout <- create_layout(g, layout = "stress")

plot_network_with_sr <- sim_sr_matrix_asymptotic %>%
  mutate(
    from_sorted = if_else(from < to, from, to),
    to_sorted = if_else(from < to, to, from)
  ) %>%
  group_by(gamma, from = from_sorted, to = to_sorted) %>%
  summarise(sr_value = mean(sr_value)) %>%
  filter(round(gamma, 1) %in% c(0.1, 0.5, 0.9)) %>%
  filter(sr_value > 0.05) %>%
  left_join(adjlist) %>%
  filter(from < to) %>%
  mutate(
    edge = factor(edge),
    gamma = str_c(unicode_greek["gamma"], " = ", gamma)
  ) %>%
  tbl_graph(edges = ., directed = FALSE) %>%
  mutate(name = row_number()) %>%
  ggraph("manual", x=butterfly_layout$x, y=butterfly_layout$y) +
  theme_network() +
  facet_edges(~gamma, ncol = 1) +
  geom_edge_link(aes(alpha = sr_value, color = edge)) +
  geom_node_label(aes(label = name)) +
  scale_edge_color_manual(
    name = NULL,
    values = c("0"="red", "1"="black"),
    labels = c("0"="Inferred connections", "1"="Observed connections")
  ) +
  scale_edge_alpha(name = "p(Target | Source)") +
  theme(
    legend.position = "bottom",
    legend.box = "vertical",
    strip.background = element_blank(),
    strip.text = element_text(size = 13)
  ) +
  ggtitle("Cognitive maps predicted by SR")
## `summarise()` has grouped output by 'gamma', 'from'. You can override using the `.groups` argument.
## Joining with `by = join_by(from, to)`
plot_network_with_sr

if (knitting) {
  suppressWarnings(
    ggsave(
      here("figures", str_c("network_with_sr", ".pdf")),
      plot = plot_network_with_sr,
      width = 3.5, height = 6,
      units = "in", dpi = 300
    )
  )
  
  ggsave(
    here("figures", str_c("network_with_sr_cairo", ".pdf")),
    plot = plot_network_with_sr,
    width = 3.5, height = 6,
    units = "in", dpi = 300,
    device = cairo_pdf
  )
}

Transition reevaluation

Following transition reevaluation, a caching account predicts that people’s navigation should get worse relative to their performance after overnight rest. In contrast, a planning account predicts that people’s navigation should not be greatly impacted by a relatively small set of changes.

stats_nav_tr_dist2 <- bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2", "D2b")) %>%
  mutate(
    measurement_id = fct_relevel(measurement_id, "D2b"),
    shortest_path = fct_relevel(shortest_path, "2")
  ) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

stats_nav_tr_dist3 <- bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2", "D2b")) %>%
  mutate(
    measurement_id = fct_relevel(measurement_id, "D2b"),
    shortest_path = fct_relevel(shortest_path, "3")
  ) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

stats_nav_tr_dist4 <- bind_rows(nav_study2, nav_study3) %>%
  filter(measurement_id %in% c("D1", "D2", "D2b")) %>%
  mutate(
    measurement_id = fct_relevel(measurement_id, "D2b"),
    shortest_path = fct_relevel(shortest_path, "4")
  ) %>%
  glmmTMB(
    correct ~ shortest_path * measurement_id +
      (1 + shortest_path + measurement_id | sub_id) + (1 | study),
    family = binomial,
    data = .
  )

map_dfr(
  .x = list(
    "dist-2" = stats_nav_tr_dist2,
    "dist-3" = stats_nav_tr_dist3,
    "dist-4" = stats_nav_tr_dist4
  ),
  .f = ~tidy(.x, conf.int = TRUE),
  .id = "ref_cat"
) %>%
  check_significance() %>%
  select(-c(ref_cat, effect, component)) %>%
  kbl(
    caption = str_c(
      "<center>", "Navigation accuracy: Transition Reevaluation", "</center>"
    ),
    digits = 3
  ) %>%
  kable_styling(bootstrap_options = c("responsive")) %>%
  pack_rows("Ref. Cat. dist-2", 1, 25) %>%
  pack_rows("Ref. Cat. dist-3", 26, 50) %>%
  pack_rows("Ref. Cat. dist-4", 51, 75)
Navigation accuracy: Transition Reevaluation
group term estimate std.error statistic p.value conf.low conf.high sig
Ref. Cat. dist-2
NA (Intercept) 1.600 0.110 14.558 0.000 1.385 1.815 ***
NA shortest_path3 -0.536 0.075 -7.183 0.000 -0.682 -0.390 ***
NA shortest_path4 -1.029 0.080 -12.837 0.000 -1.187 -0.872 ***
NA measurement_idD1 0.038 0.066 0.584 0.559 -0.091 0.168
NA measurement_idD2 0.181 0.068 2.657 0.008 0.048 0.315 **
NA shortest_path3:measurement_idD1 0.052 0.077 0.672 0.502 -0.099 0.203
NA shortest_path4:measurement_idD1 0.088 0.074 1.183 0.237 -0.058 0.234
NA shortest_path3:measurement_idD2 0.071 0.079 0.902 0.367 -0.083 0.225
NA shortest_path4:measurement_idD2 0.287 0.076 3.765 0.000 0.137 0.436 ***
sub_id sd__(Intercept) 0.728 NA NA NA 0.583 0.910
sub_id sd__shortest_path3 0.369 NA NA NA 0.267 0.509
sub_id sd__shortest_path4 0.437 NA NA NA 0.339 0.564
sub_id sd__measurement_idD1 0.275 NA NA NA 0.193 0.391
sub_id sd__measurement_idD2 0.285 NA NA NA 0.200 0.407
sub_id cor__(Intercept).shortest_path3 0.055 NA NA NA -0.307 0.397
sub_id cor__(Intercept).shortest_path4 -0.353 NA NA NA -0.936 0.071
sub_id cor__(Intercept).measurement_idD1 -0.296 NA NA NA -0.440 0.119
sub_id cor__(Intercept).measurement_idD2 0.414 NA NA NA -0.036 0.488
sub_id cor__shortest_path3.shortest_path4 0.865 NA NA NA 0.194 0.932
sub_id cor__shortest_path3.measurement_idD1 -0.181 NA NA NA -0.216 0.273
sub_id cor__shortest_path3.measurement_idD2 0.171 NA NA NA -0.246 0.510
sub_id cor__shortest_path4.measurement_idD1 -0.103 NA NA NA 0.245 0.333
sub_id cor__shortest_path4.measurement_idD2 -0.044 NA NA NA -0.188 0.433
sub_id cor__measurement_idD1.measurement_idD2 0.356 NA NA NA 0.560 0.808
study sd__(Intercept) 0.028 NA NA NA 0.004 0.181
Ref. Cat. dist-3
NA (Intercept) 1.064 0.124 8.567 0.000 0.821 1.307 ***
NA shortest_path2 0.536 0.075 7.183 0.000 0.390 0.682 ***
NA shortest_path4 -0.493 0.061 -8.036 0.000 -0.614 -0.373 ***
NA measurement_idD1 0.090 0.071 1.277 0.201 -0.048 0.229
NA measurement_idD2 0.252 0.073 3.446 0.001 0.109 0.396 ***
NA shortest_path2:measurement_idD1 -0.052 0.077 -0.672 0.501 -0.203 0.099
NA shortest_path4:measurement_idD1 0.036 0.078 0.459 0.646 -0.118 0.190
NA shortest_path2:measurement_idD2 -0.071 0.079 -0.902 0.367 -0.225 0.083
NA shortest_path4:measurement_idD2 0.216 0.080 2.684 0.007 0.058 0.374 **
sub_id sd__(Intercept) 0.834 NA NA NA 0.664 1.048
sub_id sd__shortest_path2 0.369 NA NA NA 0.267 0.509
sub_id sd__shortest_path4 0.220 NA NA NA 0.137 0.353
sub_id sd__measurement_idD1 0.275 NA NA NA 0.193 0.391
sub_id sd__measurement_idD2 0.285 NA NA NA 0.200 0.407
sub_id cor__(Intercept).shortest_path2 -0.490 NA NA NA -0.717 -0.095
sub_id cor__(Intercept).shortest_path4 -0.675 NA NA NA -0.753 0.183
sub_id cor__(Intercept).measurement_idD1 -0.338 NA NA NA -0.491 0.099
sub_id cor__(Intercept).measurement_idD2 0.437 NA NA NA -0.051 0.528
sub_id cor__shortest_path2.shortest_path4 -0.042 NA NA NA 0.134 0.401
sub_id cor__shortest_path2.measurement_idD1 0.181 NA NA NA 0.180 0.357
sub_id cor__shortest_path2.measurement_idD2 -0.172 NA NA NA -0.180 0.211
sub_id cor__shortest_path4.measurement_idD1 0.099 NA NA NA 0.324 0.614
sub_id cor__shortest_path4.measurement_idD2 -0.376 NA NA NA -0.005 0.537
sub_id cor__measurement_idD1.measurement_idD2 0.356 NA NA NA 0.549 0.793
study sd__(Intercept) 0.028 NA NA NA 0.004 0.181
Ref. Cat. dist-4
NA (Intercept) 0.571 0.106 5.360 0.000 0.362 0.779 ***
NA shortest_path2 1.029 0.080 12.836 0.000 0.872 1.187 ***
NA shortest_path3 0.493 0.061 8.036 0.000 0.373 0.614 ***
NA measurement_idD1 0.126 0.067 1.898 0.058 -0.004 0.257 .
NA measurement_idD2 0.468 0.070 6.722 0.000 0.332 0.605 ***
NA shortest_path2:measurement_idD1 -0.088 0.074 -1.183 0.237 -0.234 0.058
NA shortest_path3:measurement_idD1 -0.036 0.078 -0.459 0.646 -0.190 0.118
NA shortest_path2:measurement_idD2 -0.287 0.076 -3.765 0.000 -0.436 -0.137 ***
NA shortest_path3:measurement_idD2 -0.216 0.080 -2.684 0.007 -0.374 -0.058 **
sub_id sd__(Intercept) 0.705 NA NA NA 0.559 0.889
sub_id sd__shortest_path2 0.437 NA NA NA 0.339 0.564
sub_id sd__shortest_path3 0.220 NA NA NA 0.137 0.353
sub_id sd__measurement_idD1 0.275 NA NA NA 0.193 0.391
sub_id sd__measurement_idD2 0.285 NA NA NA 0.200 0.407
sub_id cor__(Intercept).shortest_path2 -0.256 NA NA NA -0.545 0.121
sub_id cor__(Intercept).shortest_path3 0.487 NA NA NA -0.449 0.629
sub_id cor__(Intercept).measurement_idD1 -0.370 NA NA NA -0.578 0.074
sub_id cor__(Intercept).measurement_idD2 0.400 NA NA NA -0.098 0.476
sub_id cor__shortest_path2.shortest_path3 0.538 NA NA NA 0.141 0.799
sub_id cor__shortest_path2.measurement_idD1 0.103 NA NA NA 0.104 0.288
sub_id cor__shortest_path2.measurement_idD2 0.044 NA NA NA -0.147 0.342
sub_id cor__shortest_path3.measurement_idD1 -0.099 NA NA NA -0.110 0.437
sub_id cor__shortest_path3.measurement_idD2 0.376 NA NA NA -0.487 0.641
sub_id cor__measurement_idD1.measurement_idD2 0.356 NA NA NA 0.407 0.819
study sd__(Intercept) 0.028 NA NA NA 0.004 0.181
predict_tr <- expand_grid(
  measurement_id = c("D1", "D2", "D2b"),
  shortest_path = factor(2:4),
  sub_id = NA, study = NA
) %>%
  predict_glmmTMB(stats_nav_tr_dist2)

plot_nav_tr <- nav_study3 %>%
  filter(measurement_id %in% c("D1", "D2", "D2b")) %>%
  group_by(sub_id, measurement_id, shortest_path) %>%
  summarise(accuracy = mean(correct), .groups = "drop") %>%
  ggplot(aes(x=shortest_path, y=accuracy, color=measurement_id)) +
  theme_custom() +
  geom_hline(yintercept = 0.5, linetype = "dashed") +
  geom_dotplot(
    aes(fill = measurement_id),
    binwidth = 0.01,
    binaxis = "y", stackdir = "center",
    position = position_dodge(width = 0.75),
    dotsize = 1, alpha = 0.5, color = NA,
    show.legend = FALSE
  ) +
  geom_pointrange(
    aes(
      x = shortest_path, y = fit,
      ymin = fit - se.fit, ymax = fit + se.fit,
      color = measurement_id
    ),
    data = predict_tr, inherit.aes = FALSE, show.legend = FALSE,
    position = position_dodge(width = 0.25), linewidth = 1
  ) +
  geom_line(
    aes(
      x = shortest_path, y = fit,
      group = measurement_id, color = measurement_id
    ),
    data = predict_tr, inherit.aes = FALSE,
    position = position_dodge(width = 0.25), linewidth = 1
  ) +
  scale_x_discrete(name = "Shortest path distance") +
  scale_y_continuous(
    name = "Accuracy", labels = scales::percent, breaks = seq(0, 1, 0.25)
  ) +
  scale_color_manual(
    name = "Measurement",
    values = c("D1"="#fa9fb5", "D2"="#7a0177", "D2b"="#238b45"),
    labels = c(
      "D1"="Before overnight rest",
      "D2"="After overnight rest",
      "D2b"="After reevaluation"
    )
  ) +
  scale_fill_manual(
    values = c("D1"="#fa9fb5", "D2"="#7a0177", "D2b"="#238b45")
  ) +
  coord_cartesian(ylim = c(0.3, 1.1)) +
  theme(legend.position = "bottom") +
  ggtitle("Navigation after transition reevaluation")

plot_nav_tr

if (knitting) {
  ggsave(
    here("figures", str_c("navigation_reevaluation", ".pdf")),
    plot = plot_nav_tr,
    width = 4, height = 4,
    units = "in", dpi = 300
  )
}

Session info

For reproducibility.

sessionInfo()
## R version 4.3.1 (2023-06-16)
## Platform: aarch64-apple-darwin20 (64-bit)
## Running under: macOS Sonoma 14.2
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: America/New_York
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] tictoc_1.2          kableExtra_1.3.4    ggraph_2.1.0       
##  [4] tidygraph_1.2.3     broom.mixed_0.2.9.4 glmmTMB_1.1.7      
##  [7] here_1.0.1          lubridate_1.9.2     forcats_1.0.0      
## [10] stringr_1.5.0       dplyr_1.1.2         purrr_1.0.2        
## [13] readr_2.1.4         tidyr_1.3.0         tibble_3.2.1       
## [16] ggplot2_3.4.3       tidyverse_2.0.0    
## 
## loaded via a namespace (and not attached):
##  [1] tidyselect_1.2.0    viridisLite_0.4.2   farver_2.1.1       
##  [4] viridis_0.6.4       fastmap_1.1.1       tweenr_2.0.2       
##  [7] digest_0.6.33       timechange_0.2.0    lifecycle_1.0.3    
## [10] magrittr_2.0.3      compiler_4.3.1      rlang_1.1.1        
## [13] sass_0.4.7          tools_4.3.1         igraph_1.5.1       
## [16] utf8_1.2.3          yaml_2.3.7          knitr_1.43         
## [19] labeling_0.4.2      graphlayouts_1.0.0  bit_4.0.5          
## [22] xml2_1.3.5          withr_2.5.0         numDeriv_2016.8-1.1
## [25] grid_4.3.1          polyclip_1.10-4     fansi_1.0.4        
## [28] colorspace_2.1-0    future_1.33.0       globals_0.16.2     
## [31] scales_1.2.1        MASS_7.3-60         cli_3.6.1          
## [34] crayon_1.5.2        rmarkdown_2.24      ragg_1.2.5         
## [37] generics_0.1.3      rstudioapi_0.15.0   httr_1.4.7         
## [40] tzdb_0.4.0          minqa_1.2.5         cachem_1.0.8       
## [43] ggforce_0.4.1       splines_4.3.1       rvest_1.0.3        
## [46] parallel_4.3.1      vctrs_0.6.3         boot_1.3-28.1      
## [49] webshot_0.5.5       Matrix_1.6-1        jsonlite_1.8.7     
## [52] hms_1.1.3           bit64_4.0.5         ggrepel_0.9.3      
## [55] listenv_0.9.0       systemfonts_1.0.4   jquerylib_0.1.4    
## [58] glue_1.6.2          parallelly_1.36.0   nloptr_2.0.3       
## [61] codetools_0.2-19    stringi_1.7.12      gtable_0.3.3       
## [64] lme4_1.1-34         munsell_0.5.0       furrr_0.3.1        
## [67] pillar_1.9.0        htmltools_0.5.6     R6_2.5.1           
## [70] TMB_1.9.6           textshaping_0.3.6   rprojroot_2.0.3    
## [73] vroom_1.6.3         evaluate_0.21       lattice_0.21-8     
## [76] highr_0.10          backports_1.4.1     broom_1.0.5        
## [79] bslib_0.5.1         Rcpp_1.0.11         svglite_2.1.1      
## [82] gridExtra_2.3       nlme_3.1-162        mgcv_1.8-42        
## [85] xfun_0.40           pkgconfig_2.0.3
LS0tCnRpdGxlOiAiQW5hbHlzZXMgZm9yIG1haW4gdGV4dCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKLS0tCgojIFNldHVwCgpgYGB7ciBsaWJyYXJpZXN9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGhlcmUpCgpsaWJyYXJ5KGdsbW1UTUIpCmxpYnJhcnkoYnJvb20ubWl4ZWQpCgpsaWJyYXJ5KHRpZHlncmFwaCkKbGlicmFyeShnZ3JhcGgpCgpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkodGljdG9jKQpgYGAKCmBgYHtyIGN1c3RvbS1mdW5jdGlvbnN9CnNvdXJjZShoZXJlKCJjb2RlIiwgInV0aWxzIiwgImdncGxvdF90aGVtZXMuUiIpKQpzb3VyY2UoaGVyZSgiY29kZSIsICJ1dGlscyIsICJtb2RlbGluZ191dGlscy5SIikpCnNvdXJjZShoZXJlKCJjb2RlIiwgInV0aWxzIiwgInJlcHJlc2VudGF0aW9uX3V0aWxzLlIiKSkKc291cmNlKGhlcmUoImNvZGUiLCAidXRpbHMiLCAidW5pY29kZV9ncmVlay5SIikpCgpwcmVkaWN0X2dsbW1UTUIgPC0gZnVuY3Rpb24obWFrZV9wcmVkaWN0aW9uc19mb3IsIG1vZGVsX29iamVjdCkgewogIG1ha2VfcHJlZGljdGlvbnNfZm9yICU+JQogICAgYmluZF9jb2xzKAogICAgICBwcmVkaWN0KAogICAgICAgIG9iamVjdCA9IG1vZGVsX29iamVjdCwKICAgICAgICBuZXdkYXRhID0gLiwKICAgICAgICByZS5mb3JtID0gTkEsIGFsbG93Lm5ldy5sZXZlbHMgPSBUUlVFLCBzZS5maXQgPSBUUlVFLCB0eXBlID0gInJlc3BvbnNlIgogICAgICApCiAgICApCn0KCmNoZWNrX3NpZ25pZmljYW5jZSA8LSBmdW5jdGlvbih0aWR5X2RmKSB7CiAgdGlkeV9kZiAlPiUKICAgIG11dGF0ZSgKICAgICAgc2lnID0gY2FzZV93aGVuKAogICAgICAgIHAudmFsdWUgPCAwLjAwMSB+ICIqKioiLAogICAgICAgIHAudmFsdWUgPCAwLjAxIH4gIioqIiwKICAgICAgICBwLnZhbHVlIDwgMC4wNSB+ICIqIiwKICAgICAgICBwLnZhbHVlIDwgMC4xIH4gIi4iLAogICAgICAgIFRSVUUgfiAiIgogICAgICApCiAgICApCn0KYGBgCgpgYGB7ciBrbml0dGluZy1zZXR1cH0KIyBUbyBjb250cm9sIHdoZW4gdG8gc2F2ZSBmaWd1cmVzCmtuaXR0aW5nIDwtIGtuaXRyOjppc19odG1sX291dHB1dCgpCgppZiAoa25pdHRpbmcpIHsKICBpZiAoIWRpci5leGlzdHMoaGVyZSgiZmlndXJlcyIpKSkgewogICAgZGlyLmNyZWF0ZShoZXJlKCJmaWd1cmVzIikpCiAgfQp9CmBgYAoKCiMgTmV0d29yayB2aXN1YWxpemF0aW9ucwoKYGBge3IgbG9hZC1uZXR3b3JrLWRhdGF9CmFkamxpc3QgPC0gaGVyZSgiZGF0YSIsICJjbGVhbi1kYXRhIiwgImFkamxpc3RfbGVhcm5lZC5jc3YiKSAlPiUKICByZWFkX2NzdihzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKZyA8LSBhZGpsaXN0ICU+JQogIGZpbHRlcihmcm9tIDwgdG8sIGVkZ2UgPT0gMSkgJT4lCiAgc2VsZWN0KC1lZGdlKSAlPiUKICB0YmxfZ3JhcGgoZWRnZXMgPSAuLCBkaXJlY3RlZCA9IEZBTFNFKQoKYWRqbGlzdF9yZWV2YWx1YXRlZCA8LSBoZXJlKCJkYXRhIiwgImNsZWFuLWRhdGEiLCAiYWRqbGlzdF9yZWV2YWx1YXRlZC5jc3YiKSAlPiUKICByZWFkX2NzdihzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKZ19yZWV2YWx1YXRlZCA8LSBhZGpsaXN0X3JlZXZhbHVhdGVkICU+JQogIGZpbHRlcihmcm9tIDwgdG8sIGVkZ2UgPT0gMSkgJT4lCiAgc2VsZWN0KC1lZGdlKSAlPiUKICB0YmxfZ3JhcGgoZWRnZXMgPSAuLCBkaXJlY3RlZCA9IEZBTFNFKQpgYGAKCmBgYHtyIHBsb3QtbmV0d29ya3N9CnBsb3RfbmV0d29ya19sZWFybmVkIDwtIGcgJT4lCiAgbXV0YXRlKG5hbWUgPSByb3dfbnVtYmVyKCkpICU+JQogIGdncmFwaCgic3RyZXNzIikgKwogIHRoZW1lX25ldHdvcmsoKSArCiAgZ2VvbV9lZGdlX2xpbmsoKSArCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IG5hbWUpKQoKcGxvdF9uZXR3b3JrX3JlZXZhbHVhdGVkIDwtIGdfcmVldmFsdWF0ZWQgJT4lCiAgbXV0YXRlKG5hbWUgPSByb3dfbnVtYmVyKCkpICU+JQogIGdncmFwaCgic3RyZXNzIikgKwogIHRoZW1lX25ldHdvcmsoKSArCiAgZ2VvbV9lZGdlX2xpbmsoKSArCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IG5hbWUpKQoKcGxvdF9uZXR3b3JrX2xlYXJuZWQKcGxvdF9uZXR3b3JrX3JlZXZhbHVhdGVkCgppZiAoa25pdHRpbmcpIHsKICBnZ3NhdmUoCiAgICBoZXJlKCJmaWd1cmVzIiwgc3RyX2MoIm5ldHdvcmtfbGVhcm5lZCIsICIucGRmIikpLAogICAgcGxvdCA9IHBsb3RfbmV0d29ya19sZWFybmVkLAogICAgd2lkdGggPSA0LCBoZWlnaHQgPSAyLAogICAgdW5pdHMgPSAiaW4iLCBkcGkgPSAzMDAKICApCiAgCiAgZ2dzYXZlKAogICAgaGVyZSgiZmlndXJlcyIsIHN0cl9jKCJuZXR3b3JrX3JlZXZhbHVhdGVkIiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9uZXR3b3JrX3JlZXZhbHVhdGVkLAogICAgd2lkdGggPSA0LCBoZWlnaHQgPSAyLAogICAgdW5pdHMgPSAiaW4iLCBkcGkgPSAzMDAKICApCn0KYGBgCgoKIyBDYW4gaHVtYW5zIHNvbHZlIHNvY2lhbCBuYXZpZ2F0aW9uIHByb2JsZW1zPwoKSW4gdGhlIHBhcGVyLCB3ZSBzdGFydCBieSBleGFtaW5pbmcgc29jaWFsIG5hdmlnYXRpb24gYmVoYXZpb3JzIGluIGEgb25lLWRheSBzZXNzaW9uIChTdHVkeSAxKSwgb3IgaW4gdGhlIGZpcnN0IHNlc3Npb24gb2YgYSB0d28tZGF5IHN0dWR5IChTdHVkaWVzIDItMykuIEFjcm9zcyBhbGwgdGhyZWUgc3R1ZGllcywgdGhlIHByb2NlZHVyZSBpcyBleGFjdGx5IGlkZW50aWNhbDsgc3R1ZGllcyAyLTMgYXJlLCBpbiB0aGlzIHBhcnQgb2YgdGhlIGRhdGFzZXQsIGV4YWN0IHJlcGxpY2F0aW9ucyBvZiBzdHVkeSAxLgoKYGBge3IgbG9hZC1uYXZpZ2F0aW9uLWRhdGF9Cm5hdl9zdHVkeTEgPC0gaGVyZSgiZGF0YSIsICJjbGVhbi1kYXRhIiwgInN0dWR5MV9tZXNzYWdlX3Bhc3NpbmcuY3N2IikgJT4lCiAgcmVhZF9jc3Yoc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCiAgZmlsdGVyKAogICAgdHdvX2NvcnJlY3Rfb3B0aW9ucyA9PSBGQUxTRSwKICAgIHNob3J0ZXN0X3BhdGhfZ2l2ZW5fb3B0cyA9PSBzaG9ydGVzdF9wYXRoX2dpdmVuX3N0YXJ0X2VuZAogICkgJT4lCiAgbXV0YXRlKAogICAgc3R1ZHkgPSAiU3R1ZHkgMSIsCiAgICBtZWFzdXJlbWVudF9pZCA9IHN0cl9jKCJEIiwgbWVhc3VyZW1lbnRfaWQpLAogICAgc2hvcnRlc3RfcGF0aCA9IGZhY3RvcihzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMpCiAgKSAlPiUKICBzZWxlY3QoCiAgICBzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgc2hvcnRlc3RfcGF0aCwKICAgIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLAogICAgb3B0MV9pZCwgb3B0Ml9pZCwKICAgIGNvcnJlY3RfY2hvaWNlLCBzdWJfY2hvaWNlLAogICAgY29ycmVjdCwgcnQKICApCgpuYXZfc3R1ZHkyIDwtIGhlcmUoImRhdGEiLCAiY2xlYW4tZGF0YSIsICJzdHVkeTJfbWVzc2FnZV9wYXNzaW5nLmNzdiIpICU+JQogIHJlYWRfY3N2KHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQogIGZpbHRlcigKICAgIHR3b19jb3JyZWN0X29wdGlvbnMgPT0gRkFMU0UsCiAgICBzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMgPT0gc2hvcnRlc3RfcGF0aF9naXZlbl9zdGFydF9lbmQKICApICU+JQogIG11dGF0ZSgKICAgIHN0dWR5ID0gIlN0dWR5IDIiLAogICAgbWVhc3VyZW1lbnRfaWQgPSBjYXNlX3doZW4oCiAgICAgIG5ldHdvcmsgPT0gImxlYXJuZWQiIH4gc3RyX2MoIkQiLCBtZWFzdXJlbWVudF9pZCksCiAgICAgIG5ldHdvcmsgPT0gInJlZXZhbHVhdGVkIiB+ICJEMmIiCiAgICApLAogICAgc2hvcnRlc3RfcGF0aCA9IGZhY3RvcihzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMpCiAgKSAlPiUKICBzZWxlY3QoCiAgICBzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgc2hvcnRlc3RfcGF0aCwKICAgIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLAogICAgb3B0MV9pZCwgb3B0Ml9pZCwKICAgIGNvcnJlY3RfY2hvaWNlLCBzdWJfY2hvaWNlLAogICAgY29ycmVjdCwgcnQKICApCgpuYXZfc3R1ZHkzIDwtIGhlcmUoImRhdGEiLCAiY2xlYW4tZGF0YSIsICJzdHVkeTNfbWVzc2FnZV9wYXNzaW5nLmNzdiIpICU+JQogIHJlYWRfY3N2KHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQogIGZpbHRlcigKICAgIHR3b19jb3JyZWN0X29wdGlvbnMgPT0gRkFMU0UsCiAgICBzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMgPT0gc2hvcnRlc3RfcGF0aF9naXZlbl9zdGFydF9lbmQKICApICU+JQogIG11dGF0ZSgKICAgIHN0dWR5ID0gIlN0dWR5IDMiLAogICAgbWVhc3VyZW1lbnRfaWQgPSBjYXNlX3doZW4oCiAgICAgIG5ldHdvcmsgPT0gInJlZXZhbHVhdGVkIiB+ICJEMmIiLAogICAgICBtZWFzdXJlbWVudF9pZCA9PSAxIH4gIkQxIiwKICAgICAgbWVhc3VyZW1lbnRfaWQgPT0gMiB+ICJEMWIiLAogICAgICBtZWFzdXJlbWVudF9pZCA9PSAzIH4gIkQyIgogICAgKSwKICAgIHNob3J0ZXN0X3BhdGggPSBmYWN0b3Ioc2hvcnRlc3RfcGF0aF9naXZlbl9vcHRzKQogICkgJT4lCiAgc2VsZWN0KAogICAgc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgsCiAgICBzdGFydHBvaW50X2lkLCBlbmRwb2ludF9pZCwKICAgIG9wdDFfaWQsIG9wdDJfaWQsCiAgICBjb3JyZWN0X2Nob2ljZSwgc3ViX2Nob2ljZSwKICAgIGNvcnJlY3QsIHJ0CiAgKQpgYGAKCldlJ2xsIHN0YXJ0IG9mZiB3aXRoIHNvbWUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBvZiBodW1hbiBiZWhhdmlvci4gVG8gbWF4aW1pemUgc3RhdGlzdGljYWwgcG93ZXIsIHdlIHdpbGwgcG9vbCBhY3Jvc3Mgc3R1ZGllcyB3aGVuZXZlciBwb3NzaWJsZS4KCmBgYHtyIGRlc2NyaXB0aXZlLW5hdmlnYXRpb24tZGF5MX0KYmluZF9yb3dzKG5hdl9zdHVkeTEsIG5hdl9zdHVkeTIsIG5hdl9zdHVkeTMpICU+JQogIGZpbHRlcihtZWFzdXJlbWVudF9pZCA9PSAiRDEiKSAlPiUKICBncm91cF9ieShtZWFzdXJlbWVudF9pZCwgc2hvcnRlc3RfcGF0aCkgJT4lCiAgc3VtbWFyaXNlKGFjY3VyYWN5ID0gbWVhbihjb3JyZWN0KSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgYXJyYW5nZShtZWFzdXJlbWVudF9pZCwgc2hvcnRlc3RfcGF0aCkgJT4lCiAgcGl2b3Rfd2lkZXIoCiAgICBuYW1lc19mcm9tID0gc2hvcnRlc3RfcGF0aCwgdmFsdWVzX2Zyb20gPSBhY2N1cmFjeSwgbmFtZXNfcHJlZml4ID0gImRpc3QtIgogICkgJT4lCiAga2JsKAogICAgY2FwdGlvbiA9IHN0cl9jKAogICAgICAiPGNlbnRlcj4iLCAiRGVzY3JpcHRpdmU6IE5hdmlnYXRpb24gYWNjdXJhY3kiLCAiPC9jZW50ZXI+IgogICAgKSwKICAgIGRpZ2l0cyA9IDIKICApICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJyZXNwb25zaXZlIikpCmBgYAoKQW5kIG5vdyB0aGUgaW5mZXJlbnRpYWwgc3RhdGlzdGljYWwgdGVzdHMuIE5vdGUgdGhhdCB3ZSdyZSBpbnRlcmVzdGVkIGluIGtub3dpbmcgd2hldGhlciBuYXZpZ2F0aW9uIGFjY3VyYWN5IGRpZmZlcnMgZnJvbSBjaGFuY2UgYXQgZWFjaCBkaXN0YW5jZSwgc28gd2UnbGwgZXN0aW1hdGUgdGhlIHNhbWUgc3RhdGlzdGljYWwgbW9kZWwgbXVsdGlwbGUgdGltZXMsIGNoYW5naW5nIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkgZWFjaCB0aW1lLiBOb3RlIHRoYXQgdGhpcyBvbmx5IHJlcGFyYW1ldGVyaXplcyB0aGUgbW9kZWwsIHN1Y2ggdGhhdCB0aGUgKnNhbWUqIHZhcmlhbmNlIGlzIGFjY291bnRlZCBmb3IgYnkgZGlmZmVyZW50IHBhcmFtZXRlcnM7IGl0IGRvZXMgKm5vdCogY2hhbmdlIHRoZSB0b3RhbCBhbW91bnQgb2YgdmFyaWFuY2UgYWNjb3VudGVkIGZvci4KCmBgYHtyIHN0YXRzLWRheTF9Cm5hdl9kYXkxIDwtIGJpbmRfcm93cyhuYXZfc3R1ZHkxLCBuYXZfc3R1ZHkyLCBuYXZfc3R1ZHkzKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgPT0gIkQxIikgJT4lCiAgIyBHaXZlIGV2ZXJ5IHN1YmplY3QgYSBkaXN0aW5jdCBpZGVudGlmaWVyCiAgbXV0YXRlKHN1Yl9pZCA9IHN0cl9jKHN0dWR5LCAiIHMiLCBzdWJfaWQpKQoKc3RhdHNfbmF2X2RheTFfZGlzdDIgPC0gbmF2X2RheTEgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMiIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKyAoMSArIHNob3J0ZXN0X3BhdGggfCBzdWJfaWQpICsgKDEgfCBzdHVkeSksCiAgICBmYW1pbHkgPSBiaW5vbWlhbCwKICAgIGRhdGEgPSAuCiAgKQoKc3RhdHNfbmF2X2RheTFfZGlzdDMgPC0gbmF2X2RheTEgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMyIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKyAoMSArIHNob3J0ZXN0X3BhdGggfCBzdWJfaWQpICsgKDEgfCBzdHVkeSksCiAgICBmYW1pbHkgPSBiaW5vbWlhbCwKICAgIGRhdGEgPSAuCiAgKQoKc3RhdHNfbmF2X2RheTFfZGlzdDQgPC0gbmF2X2RheTEgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiNCIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKyAoMSArIHNob3J0ZXN0X3BhdGggfCBzdWJfaWQpICsgKDEgfCBzdHVkeSksCiAgICBmYW1pbHkgPSBiaW5vbWlhbCwKICAgIGRhdGEgPSAuCiAgKQoKbWFwX2RmcigKICAueCA9IGxpc3QoCiAgICAiZGlzdC0yIiA9IHN0YXRzX25hdl9kYXkxX2Rpc3QyLAogICAgImRpc3QtMyIgPSBzdGF0c19uYXZfZGF5MV9kaXN0MywKICAgICJkaXN0LTQiID0gc3RhdHNfbmF2X2RheTFfZGlzdDQKICApLAogIC5mID0gfnRpZHkoLngsIGNvbmYuaW50ID0gVFJVRSksCiAgLmlkID0gInJlZl9jYXQiCikgJT4lCiAgY2hlY2tfc2lnbmlmaWNhbmNlKCkgJT4lCiAgc2VsZWN0KC1jKHJlZl9jYXQsIGVmZmVjdCwgY29tcG9uZW50KSkgJT4lCiAga2JsKAogICAgY2FwdGlvbiA9IHN0cl9jKCI8Y2VudGVyPiIsICJOYXZpZ2F0aW9uIGFjY3VyYWN5OiBEYXkgMSIsICI8L2NlbnRlcj4iKSwKICAgIGRpZ2l0cyA9IDMKICApICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJyZXNwb25zaXZlIikpICU+JQogIHBhY2tfcm93cygiUmVmLiBDYXQuIGRpc3QtMiIsIDEsIDEwKSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTMiLCAxMSwgMjApICU+JQogIHBhY2tfcm93cygiUmVmLiBDYXQuIGRpc3QtNCIsIDIxLCAzMCkKYGBgCgpXZSdsbCBwbG90IG91dCB0aGUgcmF3IGRhdGEsIHBsdXMgbW9kZWwgcHJlZGljdGlvbnMuLi4KCmBgYHtyIHBsb3QtZGF5MX0KcHJlZGljdF9uYXZfZGF5MSA8LSBleHBhbmRfZ3JpZCgKICBtZWFzdXJlbWVudF9pZCA9ICJEMSIsCiAgc2hvcnRlc3RfcGF0aCA9IGZhY3RvcigyOjQpLAogIHN1Yl9pZCA9IE5BLCBzdHVkeSA9IE5BCikgJT4lCiAgcHJlZGljdF9nbG1tVE1CKHN0YXRzX25hdl9kYXkxX2Rpc3QyKQoKcGxvdF9uYXZfZGF5MSA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkID09ICJEMSIpICU+JQogIGdyb3VwX2J5KHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgpICU+JQogIHN1bW1hcmlzZShhY2N1cmFjeSA9IG1lYW4oY29ycmVjdCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIGdncGxvdChhZXMoeD1zaG9ydGVzdF9wYXRoLCB5PWFjY3VyYWN5KSkgKwogIHRoZW1lX2N1c3RvbSgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2RvdHBsb3QoCiAgICBiaW53aWR0aCA9IDAuMDEsCiAgICBiaW5heGlzID0gInkiLCBzdGFja2RpciA9ICJjZW50ZXIiLAogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNzUpLAogICAgZG90c2l6ZSA9IDEsIGFscGhhID0gMC41LCBjb2xvciA9IE5BLAogICAgc2hvdy5sZWdlbmQgPSBGQUxTRQogICkgKwogIGdlb21fcG9pbnRyYW5nZSgKICAgIGFlcyh4ID0gc2hvcnRlc3RfcGF0aCwgeSA9IGZpdCwgeW1pbiA9IGZpdCAtIHNlLmZpdCwgeW1heCA9IGZpdCArIHNlLmZpdCksCiAgICBkYXRhID0gcHJlZGljdF9uYXZfZGF5MSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjE1KSwgbGluZXdpZHRoID0gMQogICkgKwogIGdlb21fbGluZSgKICAgIGFlcyh4ID0gc2hvcnRlc3RfcGF0aCwgeSA9IGZpdCwgZ3JvdXAgPSBtZWFzdXJlbWVudF9pZCksCiAgICBkYXRhID0gcHJlZGljdF9uYXZfZGF5MSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjE1KSwgbGluZXdpZHRoID0gMQogICkgKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJTaG9ydGVzdCBwYXRoIGRpc3RhbmNlIikgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiQWNjdXJhY3kiLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjEpCiAgKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAuNCwgMS4xKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ2d0aXRsZSgiSHVtYW4gc29jaWFsIG5hdmlnYXRpb24iKQoKcGxvdF9uYXZfZGF5MQoKaWYgKGtuaXR0aW5nKSB7CiAgZ2dzYXZlKAogICAgaGVyZSgiZmlndXJlcyIsIHN0cl9jKCJuYXZpZ2F0aW9uX2RheTEiLCAiLnBkZiIpKSwKICAgIHBsb3QgPSBwbG90X25hdl9kYXkxLAogICAgd2lkdGggPSA4LzMsIGhlaWdodCA9IDMsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCgojIENvbXB1dGF0aW9uYWwgbW9kZWxzIG9mIHNvY2lhbCBuYXZpZ2F0aW9uCgpXZSdkIGxpa2UgdG8gaGF2ZSBzb21lIG1lY2hhbmlzdGljIGluc2lnaHRzIGFib3V0IGhvdyBwZW9wbGUgc29sdmUgbmF2aWdhdGlvbiBwcm9ibGVtcy4gVG8gZG8gdGhpcywgd2UnbGwgbG9vayBhdCB0d28gY2FuZGlkYXRlIG1vZGVscyBvZiBuYXZpZ2F0aW9uOiBicmVhZHRoLWZpcnN0IHNlYXJjaCAoQkZTKSBhbmQgdGhlIFN1Y2Nlc3NvciBSZXByZXNlbnRhdGlvbiAoU1IpLgoKIyMgU2ltdWxhdGlvbnMKCiMjIyBCRlMgc2ltdWxhdGlvbgoKYGBge3IgbG9hZC1iZnMtZGF0YX0Kc2ltX2JmcyA8LSBoZXJlKCJkYXRhIiwgImJmcy1zaW1zIiwgImJmc19zaW1zX2xlYXJuZWQuY3N2IikgJT4lCiAgcmVhZF9jc3Yoc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCiAgZmlsdGVyKAogICAgdHdvX2NvcnJlY3Rfb3B0aW9ucyA9PSBGQUxTRSwKICAgIHNob3J0ZXN0X3BhdGhfZ2l2ZW5fb3B0cyA9PSBzaG9ydGVzdF9wYXRoX2dpdmVuX3N0YXJ0X2VuZAogICkgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmYWN0b3Ioc2hvcnRlc3RfcGF0aF9naXZlbl9vcHRzKSkgJT4lCiAgc2VsZWN0KAogICAgc2hvcnRlc3RfcGF0aCwgc3RhcnRwb2ludF9pZCwgZW5kcG9pbnRfaWQsIG9wdDFfaWQsIG9wdDJfaWQsCiAgICBiZnNfY2hvaWNlLCBiZnNfY29ycmVjdF9jaG9pY2UsIGJmc19uX3Zpc2l0c190b3RhbAogICkKYGBgCgpJbiBvdXIgaW1wbGVtZW50YXRpb24sIHdlIG1vZGVsIHRoZSBCRlMgYWdlbnQgaGF2aW5nIHNvbWUgInRocmVzaG9sZCIgZm9yIHNlYXJjaGluZyB0aHJvdWdoIHRoZSBuZXR3b3JrLiBUaGlzIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgIndpbGxpbmduZXNzIHRvIHNwZW5kIHRpbWUvZWZmb3J0IHBlcmZvcm1pbmcgYSBzZWFyY2giIHRocmVzaG9sZC4gT25jZSB0aGF0IHRocmVzaG9sZCBpcyBleGNlZWRlZCwgaXQgYmVjb21lcyBpbmNyZWFzaW5nbHkgbGlrZWx5IHRoYXQgdGhlIGFnZW50IGdpdmVzIHVwIGFuZCBjaG9vc2VzIHJhbmRvbWx5LgoKVG8gc2VlIHdoYXQgdGhyZXNob2xkIHZhbHVlcyBtaWdodCBiZSBpbmZvcm1hdGl2ZSB0byBsb29rIGF0LCB3ZSdsbCBsb29rIGF0IHRoZSBhdmVyYWdlIG51bWJlciBvZiAic2VhcmNoZXMiIHRoYXQgYW4gYWdlbnQgbXVzdCBwZXJmb3JtIHRvIG1ha2UgYSAobm9uLXJhbmRvbSkgZGVjaXNpb24uCgpgYGB7ciBhdmctYmZzLXZpc2l0c30Kc2ltX2JmcyAlPiUKICBncm91cF9ieShzaG9ydGVzdF9wYXRoKSAlPiUKICBzdW1tYXJpc2UoYXZnX25fc2VhcmNoZXMgPSBtZWFuKGJmc19uX3Zpc2l0c190b3RhbCkpICU+JQogIGtibCgKICAgIGNhcHRpb24gPSBzdHJfYygiPGNlbnRlcj4iLCAiQXZlcmFnZSBzZWFyY2hlcyBpbiBvbmxpbmUtQkZTIiwgIjwvY2VudGVyPiIpLAogICAgZGlnaXRzID0gMgogICkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInJlc3BvbnNpdmUiKSkKYGBgCgpBbmQgbm93IHdlJ2xsIHBsb3QgdGhlIG1vZGVsIHByZWRpY3Rpb25zLi4uCgpgYGB7ciBwbG90LWJmcy1zaW19CmJmc19hdmdfYWNjdXJhY3kgPC0gc2ltX2JmcyAlPiUKICBncm91cF9ieShzaG9ydGVzdF9wYXRoLCBzdGFydHBvaW50X2lkLCBlbmRwb2ludF9pZCwgb3B0MV9pZCwgb3B0Ml9pZCkgJT4lCiAgc3VtbWFyaXNlKAogICAgYmZzX2FjY3VyYWN5ID0gbWVhbihiZnNfY29ycmVjdF9jaG9pY2UpLAogICAgYmZzX3Zpc2l0cyA9IG1lYW4oYmZzX25fdmlzaXRzX3RvdGFsKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgpwbG90X3NpbV9iZnMgPC0gYmZzX2F2Z19hY2N1cmFjeSAlPiUKICBleHBhbmRfZ3JpZChzZWFyY2hfdGhyZXNob2xkID0gc2VxKDIsIDEyLCAyKSkgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZSgKICAgICMgTm90ZTogcChCRlMpIGlzIDEtcChnaXZlIHVwKQogICAgcF9iZnMgPSBzb2Z0bWF4KAogICAgICBvcHRpb25fdmFsdWVzID0gYyhzZWFyY2hfdGhyZXNob2xkLCBiZnNfdmlzaXRzKSwKICAgICAgb3B0aW9uX2Nob3NlbiA9IDEsCiAgICAgIHRlbXBlcmF0dXJlID0gMQogICAgKQogICkgJT4lCiAgdW5ncm91cCgpICU+JQogICMgV2VpZ2ggdGhlIG1vZGVsIHByZWRpY3Rpb25zIGFjY29yZGluZyB0byB0aGVpciBsaWtlbGlob29kCiAgbXV0YXRlKAogICAgcF9naXZlX3VwID0gMSAtIHBfYmZzLAogICAgYmZzX3RocmVzaG9sZF9hY2N1cmFjeSA9IChwX2JmcyAqIGJmc19hY2N1cmFjeSkgKyAocF9naXZlX3VwICogKDEvMikpCiAgKSAlPiUKICAjIEZvcm1hdCBmb3IgcGxvdHRpbmcKICBtdXRhdGUoCiAgICBzZWFyY2hfdGhyZXNob2xkID0gc3RyX3BhZChzZWFyY2hfdGhyZXNob2xkLCB3aWR0aCA9IDIsIHNpZGUgPSAibGVmdCIpCiAgKSAlPiUKICBiaW5kX3Jvd3MoCiAgICBiZnNfYXZnX2FjY3VyYWN5ICU+JQogICAgICBtdXRhdGUoCiAgICAgICAgc2VhcmNoX3RocmVzaG9sZCA9ICJOZXZlciBnaXZlcyB1cCIsCiAgICAgICAgYmZzX3RocmVzaG9sZF9hY2N1cmFjeSA9IGJmc19hY2N1cmFjeQogICAgICApCiAgKSAlPiUKICAjIE5vdyBwbG90CiAgZ2dwbG90KAogICAgYWVzKAogICAgICB4PXNob3J0ZXN0X3BhdGgsIHk9YmZzX3RocmVzaG9sZF9hY2N1cmFjeSwKICAgICAgY29sb3I9c2VhcmNoX3RocmVzaG9sZCwgZ3JvdXA9c2VhcmNoX3RocmVzaG9sZAogICAgKQogICkgKwogIHRoZW1lX2N1c3RvbSgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJsaW5lIiwgZnVuID0gbWVhbiwgbGluZXdpZHRoID0gMSkgKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJTaG9ydGVzdCBwYXRoIGRpc3RhbmNlIikgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkFjY3VyYWN5IiwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKAogICAgbmFtZSA9ICJTZWFyY2ggdGhyZXNob2xkIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIGJlZ2luID0gMC4xLCBlbmQgPSAwLjksIGRpcmVjdGlvbiA9IC0xCiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKGJ5cm93ID0gVFJVRSwgbnJvdyA9IDEpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAuNSwgMSkpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIgogICkgKwogIGdndGl0bGUoIlNpbXVsYXRlZCBCRlMgbmF2aWdhdGlvbiIpCgpwbG90X3NpbV9iZnMKCmlmIChrbml0dGluZykgewogIGdnc2F2ZSgKICAgIGhlcmUoImZpZ3VyZXMiLCBzdHJfYygic2ltdWxhdGVkX2JmcyIsICIucGRmIikpLAogICAgcGxvdCA9IHBsb3Rfc2ltX2JmcywKICAgIHdpZHRoID0gOC8zLCBoZWlnaHQgPSAzLAogICAgdW5pdHMgPSAiaW4iLCBkcGkgPSAzMDAKICApCn0KYGBgCgojIyMgU1IgbmF2aWdhdGlvbiBzaW11bGF0aW9uCgpgYGB7ciBnZXQtdHJpYWxsaXN0fQp0cmlhbGxpc3RfbmF2X2xlYXJuZWQgPC0gbmF2X3N0dWR5MSAlPiUKICBmaWx0ZXIoc3ViX2lkID09IDEpICU+JQogIHNlbGVjdCgKICAgIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLCBvcHQxX2lkLCBvcHQyX2lkLAogICAgY29ycmVjdF9jaG9pY2UsIHNob3J0ZXN0X3BhdGgKICApICU+JQogIGFycmFuZ2Uoc3RhcnRwb2ludF9pZCwgZW5kcG9pbnRfaWQpCmBgYAoKV2UnbGwgY3JlYXRlIHNvbWUgc2ltdWxhdGVkIGxlYXJuaW5nIG9ic2VydmF0aW9ucy4KCmBgYHtyIHNpbXVsYXRlLW9ic2VydmF0aW9uc30Kc2V0LnNlZWQoc3VtKHV0ZjhUb0ludCgiSmVubnkgYW5kIG1lIHdhcyBsaWtlIHBlYXMgYW5kIGNhcnJvdHMiKSkpCgpzaW11bGF0ZWRfcGFpcmVkX2Fzc29jaWF0ZXMgPC0gYWRqbGlzdCAlPiUKICBmaWx0ZXIoZWRnZSA9PSAxKSAlPiUKICBzZWxlY3QoZnJvbSwgdG8pICU+JQogIGV4cGFuZF9ncmlkKHNldCA9IDE6NTAwMCwgLikgJT4lCiAgZ3JvdXBfYnkoc2V0KSAlPiUKICBzbGljZV9zYW1wbGUocHJvcCA9IDEpICU+JQogIHVuZ3JvdXAoKQpgYGAKCkFuZCBub3cgd2UnbGwgc2ltdWxhdGUgYW4gImFzeW1wdG90aWMiIFNSIGFuZCBob3cgaXQgcGVyZm9ybXMgaW4gdGhlIG5hdmlnYXRpb24gdGFzay4KCmBgYHtyIHNpbS1zci1hc3ltcHRvdGljfQpzaW11bGF0ZV9zciA8LSBmdW5jdGlvbihzaW11bGF0ZWRfb2JzZXJ2YXRpb25zKSB7CiAgc2ltdWxhdGVkX29ic2VydmF0aW9ucyAlPiUKICAgIGV4cGFuZF9ncmlkKGdhbW1hID0gc2VxKDAuMSwgMC45LCAwLjEpKSAlPiUKICAgIGdyb3VwX2J5KGdhbW1hKSAlPiUKICAgIG5lc3QoKSAlPiUKICAgIG11dGF0ZSgKICAgICAgc3IgPSBtYXAoCiAgICAgICAgLnggPSBkYXRhLAogICAgICAgIC5mID0gfmJ1aWxkX3JlcF9zcigKICAgICAgICAgIGxlYXJuaW5nX2RhdGEgPSAueCwgdGhpc19hbHBoYSA9IDAuMSwgdGhpc19nYW1tYSA9IGdhbW1hCiAgICAgICAgKQogICAgICApCiAgICApICU+JQogICAgdW5uZXN0KHNyKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIHNlbGVjdCgtZGF0YSkKfQoKam9pbl9zciA8LSBmdW5jdGlvbihuYXZpZ2F0aW9uX3RyaWFsbGlzdCwgc2ltdWxhdGVkX3NyKSB7CiAgbmF2aWdhdGlvbl90cmlhbGxpc3QgJT4lCiAgICBsZWZ0X2pvaW4oCiAgICAgIHNpbXVsYXRlZF9zciAlPiUKICAgICAgICByZW5hbWUob3B0MV9zciA9IHNyX3ZhbHVlLCBvcHQxX2lkID0gZnJvbSwgZW5kcG9pbnRfaWQgPSB0bykKICAgICkgJT4lCiAgICBsZWZ0X2pvaW4oCiAgICAgIHNpbXVsYXRlZF9zciAlPiUKICAgICAgICByZW5hbWUob3B0Ml9zciA9IHNyX3ZhbHVlLCBvcHQyX2lkID0gZnJvbSwgZW5kcG9pbnRfaWQgPSB0bykKICAgICkKfQoKc2ltX3NyX21hdHJpeF9hc3ltcHRvdGljIDwtIHNpbXVsYXRlZF9wYWlyZWRfYXNzb2NpYXRlcyAlPiUKICBzaW11bGF0ZV9zcigpCgpzaW1fc3JfYmVoYXZpb3JfYXN5bXB0b3RpYyA8LSBqb2luX3NyKAogIHRyaWFsbGlzdF9uYXZfbGVhcm5lZCwgc2ltX3NyX21hdHJpeF9hc3ltcHRvdGljCikgJT4lCiAgIyBGZWVkIHZhbHVlcyB0aHJvdWdoIHNvZnRtYXgKICBtdXRhdGUoYWNyb3NzKGMob3B0MV9zciwgb3B0Ml9zciksIH4ueCAqIDEwMCkpICU+JQogIGV4cGFuZF9ncmlkKHRlbXBlcmF0dXJlID0gMSkgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZSgKICAgIHBfY29ycmVjdCA9IHNvZnRtYXgoCiAgICAgIG9wdGlvbl92YWx1ZXMgPSBjKG9wdDFfc3IsIG9wdDJfc3IpLAogICAgICBvcHRpb25fY2hvc2VuID0gaWZfZWxzZShjb3JyZWN0X2Nob2ljZSA9PSBvcHQxX2lkLCAxLCAyKSwKICAgICAgdGVtcGVyYXR1cmUgPSB0ZW1wZXJhdHVyZSwKICAgICAgdXNlX2ludmVyc2VfdGVtcGVyYXR1cmUgPSBUUlVFCiAgICApCiAgKSAlPiUKICB1bmdyb3VwKCkKYGBgCgpgYGB7ciBwbG90LXNyLXNpbS1hc3ltcHRvdGljfQpwbG90X3NpbV9zcl9hc3ltcHRvdGljIDwtIHNpbV9zcl9iZWhhdmlvcl9hc3ltcHRvdGljICU+JQogIG11dGF0ZSgKICAgIGdhbW1hID0gZmFjdG9yKGdhbW1hKSwKICAgIHRlbXBlcmF0dXJlID0gc3RyX3BhZCh0ZW1wZXJhdHVyZSwgd2lkdGggPSAyLCBzaWRlID0gImxlZnQiKSwKICAgIHRlbXBlcmF0dXJlID0gc3RyX2ModW5pY29kZV9ncmVla1sidGF1Il0sICIgPSAiLCB0ZW1wZXJhdHVyZSkKICApICU+JQogIGdyb3VwX2J5KGdhbW1hLCB0ZW1wZXJhdHVyZSwgc2hvcnRlc3RfcGF0aCkgJT4lCiAgc3VtbWFyaXNlKHBfY29ycmVjdCA9IG1lYW4ocF9jb3JyZWN0KSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgZ2dwbG90KGFlcyh4PXNob3J0ZXN0X3BhdGgsIHk9cF9jb3JyZWN0LCBjb2xvcj1nYW1tYSkpICsKICB0aGVtZV9jdXN0b20oKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyh0ZW1wZXJhdHVyZSkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gZ2FtbWEsIGdyb3VwID0gZ2FtbWEpLCBsaW5ld2lkdGggPSAwLjgpICsKICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiU2hvcnRlc3QgcGF0aCBkaXN0YW5jZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJBY2N1cmFjeSIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZCgKICAgIG5hbWUgPSBzdHJfYyh1bmljb2RlX2dyZWVrWyJnYW1tYSJdLCAiID0gIiksIG9wdGlvbiA9ICJ0dXJibyIsIGVuZCA9IDAuOQogICkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChieXJvdyA9IFRSVUUsIG5yb3cgPSAxKSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLjUsIDEpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZ3RpdGxlKCJTaW11bGF0ZWQgU1IgbmF2aWdhdGlvbiIpCgpwbG90X3NpbV9zcl9hc3ltcHRvdGljCgppZiAoa25pdHRpbmcpIHsKICBzdXBwcmVzc1dhcm5pbmdzKAogICAgZ2dzYXZlKAogICAgICBoZXJlKCJmaWd1cmVzIiwgc3RyX2MoInNpbXVsYXRlZF9zciIsICIucGRmIikpLAogICAgICBwbG90ID0gcGxvdF9zaW1fc3JfYXN5bXB0b3RpYywKICAgICAgd2lkdGggPSA4LzMsIGhlaWdodCA9IDMsCiAgICAgIHVuaXRzID0gImluIiwgZHBpID0gMzAwLAogICAgKQogICkKICAKICBnZ3NhdmUoCiAgICBoZXJlKCJmaWd1cmVzIiwgc3RyX2MoInNpbXVsYXRlZF9zcl9jYWlybyIsICIucGRmIikpLAogICAgcGxvdCA9IHBsb3Rfc2ltX3NyX2FzeW1wdG90aWMgKwogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQoYnlyb3cgPSBUUlVFLCBucm93ID0gMikpLAogICAgd2lkdGggPSA4LzMsIGhlaWdodCA9IDMsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMCwKICAgIGRldmljZSA9IGNhaXJvX3BkZgogICkKfQpgYGAKCiMjIE1vZGVsIGNvbXBhcmlzb24KClRvIGRvIG1vZGVsIGNvbXBhcmlzb24sIHdlJ2xsIG5lZWQgdG8gcHVsbCBpbiB0aGUgZXN0aW1hdGVkIHBhcmFtZXRlcnMvbGlrZWxpaG9vZHMgZnJvbSB0aGUgbW9kZWwtZml0dGluZy4KCmBgYHtyIGxvYWQtcGFyYW1zfQpjbGVhbl9wYXJhbXNfZnJvbV9yYXcgPC0gRkFMU0UKCmlmIChjbGVhbl9wYXJhbXNfZnJvbV9yYXcpIHsKICBwYXJhbXMgPC0gaGVyZSgiZGF0YSIsICJwYXJhbS1maXRzIikgJT4lCiAgICBmczo6ZGlyX2xzKHJlZ2V4cCA9ICJzdHVkeVtbOmRpZ2l0Ol1dX0RbWzpkaWdpdDpdXWI/IikgJT4lCiAgICBtYXBfZGZyKAogICAgICAuZiA9IH5yZWFkX2NzdigueCwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCiAgICAgICAgYmVzdF9vcHRpbV9ydW4oImRhdGFmcmFtZSIpLAogICAgICAuaWQgPSAiZmlsZW5hbWUiCiAgICApICU+JQogICAgbXV0YXRlKAogICAgICBzdHVkeSA9IHN0cl9leHRyYWN0KGZpbGVuYW1lLCAic3R1ZHlbWzpkaWdpdDpdXSIpLAogICAgICBzdHVkeSA9IHN0cl9yZW1vdmUoc3R1ZHksICJzdHVkeSIpLAogICAgICBzdHVkeSA9IHN0cl9jKCJTdHVkeSAiLCBzdHVkeSksCiAgICAgIG1lYXN1cmVtZW50X2lkID0gc3RyX2V4dHJhY3QoZmlsZW5hbWUsICJfRFtbOmRpZ2l0Ol1dYj8iKSwKICAgICAgbWVhc3VyZW1lbnRfaWQgPSBzdHJfcmVtb3ZlKG1lYXN1cmVtZW50X2lkLCAiXyIpLAogICAgICBzdWJfaWQgPSBzdHJfZXh0cmFjdChmaWxlbmFtZSwgInN1Yi1bWzpkaWdpdDpdXSsiKSwKICAgICAgc3ViX2lkID0gc3RyX3JlbW92ZShzdWJfaWQsICJzdWItIiksCiAgICAgIHN1Yl9pZCA9IGFzLmludGVnZXIoc3ViX2lkKSwKICAgICAgbW9kZWwgPSBjYXNlX3doZW4oCiAgICAgICAgc3RyX2RldGVjdChmaWxlbmFtZSwgIl9oeWJyaWQtIikgfiAiaHlicmlkIiwKICAgICAgICBzdHJfZGV0ZWN0KGZpbGVuYW1lLCAiX3NyLSIpIH4gInNyIiwKICAgICAgICBzdHJfZGV0ZWN0KGZpbGVuYW1lLCAiX2Jmcy0iKSB+ICJiZnMiCiAgICAgICkKICAgICkgJT4lCiAgICBzZWxlY3QoCiAgICAgIHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkLAogICAgICBtb2RlbCwKICAgICAgcGFyYW1fbmFtZSwgcGFyYW1fdmFsdWUgPSBwYXJhbV92YWx1ZV9odW1hbl9yZWFkYWJsZSwKICAgICAgbmVnX2xvZ2xpayA9IG9wdGltX3ZhbHVlCiAgICApICU+JQogICAgYXJyYW5nZShzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgbW9kZWwpCiAgCiAgcGFyYW1zICU+JQogICAgd3JpdGVfY3N2KGhlcmUoImRhdGEiLCAicGFyYW0tZml0cyIsICJjbGVhbl9wYXJhbV9maXRzLmNzdiIpKQp9IGVsc2UgewogIHBhcmFtcyA8LSBoZXJlKCJkYXRhIiwgInBhcmFtLWZpdHMiLCAiY2xlYW5fcGFyYW1fZml0cy5jc3YiKSAlPiUKICAgIHJlYWRfY3N2KHNob3dfY29sX3R5cGVzID0gRkFMU0UpCn0KYGBgCgpgYGB7ciBweHAtbW9kZWwtc2VsZWN0aW9ufQpzb3VyY2UoaGVyZSgiY29kZSIsICJ1dGlscyIsICJiYXllc2lhbl9tb2RlbF9zZWxlY3Rpb24uUiIpKQoKcHhwX3Jlc3VsdHMgPC0gcGFyYW1zICU+JQogIHNlbGVjdChzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgbW9kZWwsIG5lZ19sb2dsaWspICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgbXV0YXRlKGxvZ19saWsgPSAtbmVnX2xvZ2xpaykgJT4lCiAgc2VsZWN0KC1uZWdfbG9nbGlrKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbW9kZWwsIHZhbHVlc19mcm9tID0gbG9nX2xpaykgJT4lCiAgc2VsZWN0KC1zdWJfaWQpICU+JQogIGdyb3VwX2J5KHN0dWR5LCBtZWFzdXJlbWVudF9pZCkgJT4lCiAgbmVzdCgpICU+JQogIG11dGF0ZSgKICAgIHRlc3QgPSBtYXAoCiAgICAgIC54ID0gZGF0YSwKICAgICAgLmYgPSB+YmF5ZXNpYW5fbW9kZWxfc2VsZWN0aW9uKC54KQogICAgKQogICkgJT4lCiAgdW5uZXN0KHRlc3QpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QoLWRhdGEpCgpweHBfcmVzdWx0cyAlPiUKICBnZ3Bsb3QoYWVzKHg9bWVhc3VyZW1lbnRfaWQsIHk9cHhwLCBjb2xvcj1tb2RlbCkpICsKICB0aGVtZV9jdXN0b20oKSArCiAgZmFjZXRfd3JhcCh+c3R1ZHksIHNjYWxlcyA9ICJmcmVlX3giKSArCiAgZ2VvbV9wb2ludCgKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjc1KQogICkKCnB4cF9yZXN1bHRzICU+JQogIHNlbGVjdChzdHVkeSwgbWVhc3VyZW1lbnRfaWQsIG1vZGVsLCBweHApICU+JQogIG11dGF0ZShweHAgPSBhcy5udW1lcmljKHB4cCkpICU+JQogIHBpdm90X3dpZGVyKAogICAgbmFtZXNfZnJvbSA9IG1vZGVsLAogICAgdmFsdWVzX2Zyb20gPSBweHAsCiAgICBuYW1lc19wcmVmaXggPSAicHhwXyIKICApICU+JQogIGtibCgKICAgIGNhcHRpb24gPSBzdHJfYygiPGNlbnRlcj4iLCAiUFhQIHJlc3VsdHMiLCAiPC9jZW50ZXI+IiksCiAgICBkaWdpdHMgPSAzCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygicmVzcG9uc2l2ZSIpKQpgYGAKCiMjIFBvc3RlcmlvciBwcmVkaWN0aXZlIGNoZWNrCgpXZSdsbCBzdGFydCBieSBzaW11bGF0aW5nIHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zIGZvciBlYWNoIHN1YmplY3QsIGdpdmVuIHRoZWlyIGVzdGltYXRlZCBwYXJhbWV0ZXJzLgoKYGBge3IgcHBjLWJmc30KcHBjX2JmcyA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MSwgbmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiKSkgJT4lCiAgbGVmdF9qb2luKAogICAgcGFyYW1zICU+JQogICAgICBmaWx0ZXIobW9kZWwgPT0gImJmcyIpICU+JQogICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcGFyYW1fbmFtZSwgdmFsdWVzX2Zyb20gPSBwYXJhbV92YWx1ZSkgJT4lCiAgICAgIHNlbGVjdChzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgc2VhcmNoX3RocmVzaG9sZCwgbGFwc2VfcmF0ZSkKICApICU+JQogIGxlZnRfam9pbihiZnNfYXZnX2FjY3VyYWN5KSAlPiUKICBtdXRhdGUoCiAgICBwX3N1Yl9jaG9pY2VfYmZzID0gaWZfZWxzZSgKICAgICAgc3ViX2Nob2ljZSA9PSBjb3JyZWN0X2Nob2ljZSwKICAgICAgYmZzX2FjY3VyYWN5LAogICAgICAxIC0gYmZzX2FjY3VyYWN5CiAgICApCiAgKSAlPiUKICAjIFdoYXQncyB0aGUgcHJvYmFiaWxpdHkgb2YgKmNvbXBsZXRpbmcqIEJGUy1vbmxpbmUgYWxsIHRoZSB3YXkgdGhyb3VnaD8KICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKAogICAgc2VhcmNoX3RocmVzaG9sZCA9IHNlYXJjaF90aHJlc2hvbGQsCiAgICBwX2NvbXBsZXRlX2JmcyA9IHNvZnRtYXgoCiAgICAgIG9wdGlvbl92YWx1ZXMgPSBjKHNlYXJjaF90aHJlc2hvbGQsIGJmc192aXNpdHMpLAogICAgICBvcHRpb25fY2hvc2VuID0gMSwKICAgICAgdGVtcGVyYXR1cmUgPSAxCiAgICApCiAgKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgIyBXZWlnaCBCRlMgcHJlZGljdGlvbnMgYWNjb3JkaW5nbHkKICBtdXRhdGUoCiAgICBwX2dpdmVfdXAgPSAxIC0gcF9jb21wbGV0ZV9iZnMsCiAgICBwcmVkaWN0ZWRfY29ycmVjdCA9IChwX2NvbXBsZXRlX2JmcyAqIHBfc3ViX2Nob2ljZV9iZnMpICsgKHBfZ2l2ZV91cCAqIDEvMiksCiAgICAjIyMgQWRkIGxhcHNlIHJhdGUKICAgICMgICBEaXZpZGluZyBieSAyIGlzIGJlY2F1c2UgdGhlcmUgYXJlIHR3byBvcHRpb25zIHRvIGNob29zZSBmcm9tCiAgICAjICAgVGhlcmVmb3JlLCB3aGVuIGxhcHNlIHJhdGUgPSAxLCB0aGlzIGJlY29tZXMgY2hhbmNlID0gMS8yCiAgICBwcmVkaWN0ZWRfY29ycmVjdCA9IHByZWRpY3RlZF9jb3JyZWN0ICogKDEtbGFwc2VfcmF0ZSkgKyAobGFwc2VfcmF0ZS8yKQogICkgJT4lCiAgIyBBdmVyYWdlIG92ZXIgdHJpYWxzCiAgZ3JvdXBfYnkoc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgpICU+JQogIHN1bW1hcmlzZSgKICAgIGVtcGlyaWNhbCA9IG1lYW4oY29ycmVjdCksCiAgICBwcmVkaWN0ZWQgPSBtZWFuKHByZWRpY3RlZF9jb3JyZWN0KSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCmBgYAoKYGBge3IgcHBjLXNyfQpwcGNfc3JfbWF0cml4IDwtIHBhcmFtcyAlPiUKICBmaWx0ZXIobW9kZWwgPT0gInNyIikgJT4lCiAgc2VsZWN0KAogICAgc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIG5hbWUgPSBwYXJhbV9uYW1lLCB2YWx1ZSA9IHBhcmFtX3ZhbHVlCiAgKSAlPiUKICBwaXZvdF93aWRlcigpICU+JQogIGdyb3VwX2J5KHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkKSAlPiUKICBuZXN0KCkgJT4lCiAgbXV0YXRlKAogICAgc2ltX3NyID0gbWFwKAogICAgICAueCA9IGRhdGEsCiAgICAgIC5mID0gfmJ1aWxkX3JlcF9zcigKICAgICAgICBsZWFybmluZ19kYXRhID0gc2ltdWxhdGVkX3BhaXJlZF9hc3NvY2lhdGVzICU+JSBmaWx0ZXIoc2V0ICVpbiUgMToxMDApLAogICAgICAgIHRoaXNfYWxwaGEgPSAwLjEsCiAgICAgICAgdGhpc19nYW1tYSA9IC54JHNyX2dhbW1hLAogICAgICAgIGJpZGlyZWN0aW9uYWwgPSBUUlVFCiAgICAgICkKICAgICkKICApICU+JQogIHVubmVzdChzaW1fc3IpICU+JQogIHVubmVzdChkYXRhKSAlPiUKICB1bmdyb3VwKCkKCnBwY19zcl9iZWhhdmlvciA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MSwgbmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiKSkgJT4lCiAgbGVmdF9qb2luKAogICAgcHBjX3NyX21hdHJpeCAlPiUKICAgICAgcmVuYW1lKG9wdDFfaWQgPSBmcm9tLCBlbmRwb2ludF9pZCA9IHRvLCBvcHQxX3NyID0gc3JfdmFsdWUpCiAgKSAlPiUKICBsZWZ0X2pvaW4oCiAgICBwcGNfc3JfbWF0cml4ICU+JQogICAgICByZW5hbWUob3B0Ml9pZCA9IGZyb20sIGVuZHBvaW50X2lkID0gdG8sIG9wdDJfc3IgPSBzcl92YWx1ZSkKICApICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoCiAgICBwcmVkaWN0ZWRfY29ycmVjdCA9IHNvZnRtYXgoCiAgICAgIG9wdGlvbl92YWx1ZXMgPSBjKG9wdDFfc3IsIG9wdDJfc3IpICogMTAwLAogICAgICBvcHRpb25fY2hvc2VuID0gaWZfZWxzZShjb3JyZWN0X2Nob2ljZSA9PSBvcHQxX2lkLCAxLCAyKSwKICAgICAgdGVtcGVyYXR1cmUgPSBzb2Z0bWF4X3RlbXBlcmF0dXJlLAogICAgICB1c2VfaW52ZXJzZV90ZW1wZXJhdHVyZSA9IFRSVUUsCiAgICAgIGxhcHNlX3JhdGUgPSBsYXBzZV9yYXRlCiAgICApCiAgKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgIyBGaXggdHJpYWxzIHdoZW4gdGhlIHNvZnRtYXggYmVjb21lcyB1bmRlZmluZWQKICBtdXRhdGUoCiAgICBwcmVkaWN0ZWRfY29ycmVjdCA9IGNhc2Vfd2hlbigKICAgICAgaXMubmFuKHByZWRpY3RlZF9jb3JyZWN0KSAmIChzdWJfY2hvaWNlID09IGNvcnJlY3RfY2hvaWNlKSB+IDEsCiAgICAgIGlzLm5hbihwcmVkaWN0ZWRfY29ycmVjdCkgJiAoc3ViX2Nob2ljZSAhPSBjb3JyZWN0X2Nob2ljZSkgfiAwLAogICAgICBUUlVFIH4gcHJlZGljdGVkX2NvcnJlY3QKICAgICkKICApICU+JQogICMgQXZlcmFnZSBvdmVyIHRyaWFscwogIGdyb3VwX2J5KHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkLCBzaG9ydGVzdF9wYXRoKSAlPiUKICBzdW1tYXJpc2UoCiAgICBlbXBpcmljYWwgPSBtZWFuKGNvcnJlY3QpLAogICAgcHJlZGljdGVkID0gbWVhbihwcmVkaWN0ZWRfY29ycmVjdCksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgYm90aCBQUENzLCB3ZSBjYW4gcGxvdCB0aGVtIHNpZGUtYnktc2lkZS4KCmBgYHtyIHBsb3QtcHBjLWRheTF9CnBsb3RfcHBjX2RheTEgPC0gcHBjX2JmcyAlPiUKICByZW5hbWUocHJlZGljdGVkX2JmcyA9IHByZWRpY3RlZCkgJT4lCiAgbGVmdF9qb2luKHBwY19zcl9iZWhhdmlvciAlPiUgcmVuYW1lKHByZWRpY3RlZF9zciA9IHByZWRpY3RlZCkpICU+JQogIHBpdm90X2xvbmdlcigKICAgIGMoZW1waXJpY2FsLCBwcmVkaWN0ZWRfYmZzLCBwcmVkaWN0ZWRfc3IpLAogICAgbmFtZXNfdG8gPSAiYWdlbnQiLCB2YWx1ZXNfdG8gPSAiYWNjdXJhY3kiCiAgKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgPT0gIkQxIikgJT4lCiAgZ2dwbG90KGFlcyh4PXNob3J0ZXN0X3BhdGgsIHk9YWNjdXJhY3kpKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoCiAgICBhZXMoY29sb3IgPSBhZ2VudCksCiAgICBhbHBoYSA9IDAuMDUsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKAogICAgICBqaXR0ZXIud2lkdGggPSAwLjIsIGppdHRlci5oZWlnaHQgPSAwLCBkb2RnZS53aWR0aCA9IDAuNzUsIHNlZWQgPSAxCiAgICApLAogICAgc2hvdy5sZWdlbmQgPSBGQUxTRQogICkgKwogIHN0YXRfc3VtbWFyeSgKICAgIGFlcyhjb2xvciA9IGFnZW50KSwgZ2VvbSA9ICJjcm9zc2JhciIsIGZ1biA9IG1lYW4sCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkKICApICsKICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiU2hvcnRlc3QgcGF0aCBkaXN0YW5jZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIkFjY3VyYWN5IiwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBicmVha3MgPSBzZXEoMCwgMSwgMC4yNSkKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICBuYW1lID0gTlVMTCwKICAgIHZhbHVlcyA9IGMoCiAgICAgICJlbXBpcmljYWwiPSIjZmQ4ZDNjIiwKICAgICAgInByZWRpY3RlZF9iZnMiPSIjOGMyZDA0IiwKICAgICAgInByZWRpY3RlZF9zciI9IiNiZDAwMjYiCiAgICApLAogICAgbGFiZWxzID0gYygiZW1waXJpY2FsIj0iSHVtYW4iLCAicHJlZGljdGVkX2JmcyI9IkJGUyIsICJwcmVkaWN0ZWRfc3IiPSJTUiIpCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZ3RpdGxlKCJQb3N0ZXJpb3IgcHJlZGljdGl2ZSBjaGVjayIpCgpwbG90X3BwY19kYXkxCgppZiAoa25pdHRpbmcpIHsKICBnZ3NhdmUoCiAgICBoZXJlKCJmaWd1cmVzIiwgc3RyX2MoInBwY19kYXkxIiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9wcGNfZGF5MSwKICAgIHdpZHRoID0gOC8yLCBoZWlnaHQgPSAyLjUsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCiMjIEhlbGQtb3V0IHRyaWFscwoKVGhlcmUgd2VyZSBzb21lIHRyaWFscyB3aGVyZSBib3RoIFNvdXJjZXMgaGFkIGVxdWl2YWxlbnQgc2hvcnRlc3QgcGF0aCBkaXN0YW5jZXMgZnJvbSB0aGUgVGFyZ2V0LiBXZSB3b3VsZCBleHBlY3QgdGhhdCBCRlMgd291bGQgYmUgbGFyZ2VseSBpbmRpZmZlcmVudCBiZXR3ZWVuIHRoZSB0d28gU291cmNlcywgYnV0IGl0IGlzIHBvc3NpYmxlIHRoYXQgaHVtYW5zIGFuZC9vciB0aGUgU1Igd291bGQgbWFrZSBvdGhlciBwcmVkaWN0aW9ucy4KCmBgYHtyIGxvYWQtdGllLWRhdGF9Cm5hdl9zdHVkeTFfdGllcyA8LSBoZXJlKCJkYXRhIiwgImNsZWFuLWRhdGEiLCAic3R1ZHkxX21lc3NhZ2VfcGFzc2luZy5jc3YiKSAlPiUKICByZWFkX2NzdihzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUKICBmaWx0ZXIoCiAgICB0d29fY29ycmVjdF9vcHRpb25zID09IFRSVUUsCiAgICBzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMgPT0gc2hvcnRlc3RfcGF0aF9naXZlbl9zdGFydF9lbmQKICApICU+JQogIG11dGF0ZSgKICAgIHN0dWR5ID0gIlN0dWR5IDEiLAogICAgbWVhc3VyZW1lbnRfaWQgPSBzdHJfYygiRCIsIG1lYXN1cmVtZW50X2lkKSwKICAgIHNob3J0ZXN0X3BhdGggPSBmYWN0b3Ioc2hvcnRlc3RfcGF0aF9naXZlbl9vcHRzKQogICkgJT4lCiAgc2VsZWN0KAogICAgc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgsCiAgICBzdGFydHBvaW50X2lkLCBlbmRwb2ludF9pZCwKICAgIG9wdDFfaWQsIG9wdDJfaWQsCiAgICBjb3JyZWN0X2Nob2ljZSwgc3ViX2Nob2ljZSwKICAgIGNvcnJlY3QsIHJ0CiAgKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMiIpKQoKbmF2X3N0dWR5Ml90aWVzIDwtIGhlcmUoImRhdGEiLCAiY2xlYW4tZGF0YSIsICJzdHVkeTJfbWVzc2FnZV9wYXNzaW5nLmNzdiIpICU+JQogIHJlYWRfY3N2KHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQogIGZpbHRlcigKICAgIHR3b19jb3JyZWN0X29wdGlvbnMgPT0gVFJVRSwKICAgIHNob3J0ZXN0X3BhdGhfZ2l2ZW5fb3B0cyA9PSBzaG9ydGVzdF9wYXRoX2dpdmVuX3N0YXJ0X2VuZAogICkgJT4lCiAgbXV0YXRlKAogICAgc3R1ZHkgPSAiU3R1ZHkgMiIsCiAgICBtZWFzdXJlbWVudF9pZCA9IGNhc2Vfd2hlbigKICAgICAgbmV0d29yayA9PSAibGVhcm5lZCIgfiBzdHJfYygiRCIsIG1lYXN1cmVtZW50X2lkKSwKICAgICAgbmV0d29yayA9PSAicmVldmFsdWF0ZWQiIH4gIkQyYiIKICAgICksCiAgICBzaG9ydGVzdF9wYXRoID0gZmFjdG9yKHNob3J0ZXN0X3BhdGhfZ2l2ZW5fb3B0cykKICApICU+JQogIHNlbGVjdCgKICAgIHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkLCBzaG9ydGVzdF9wYXRoLAogICAgc3RhcnRwb2ludF9pZCwgZW5kcG9pbnRfaWQsCiAgICBvcHQxX2lkLCBvcHQyX2lkLAogICAgY29ycmVjdF9jaG9pY2UsIHN1Yl9jaG9pY2UsCiAgICBjb3JyZWN0LCBydAogICkgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiKSkKCm5hdl9zdHVkeTNfdGllcyA8LSBoZXJlKCJkYXRhIiwgImNsZWFuLWRhdGEiLCAic3R1ZHkzX21lc3NhZ2VfcGFzc2luZy5jc3YiKSAlPiUKICByZWFkX2NzdihzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUKICBmaWx0ZXIoCiAgICB0d29fY29ycmVjdF9vcHRpb25zID09IFRSVUUsCiAgICBzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMgPT0gc2hvcnRlc3RfcGF0aF9naXZlbl9zdGFydF9lbmQKICApICU+JQogIG11dGF0ZSgKICAgIHN0dWR5ID0gIlN0dWR5IDMiLAogICAgbWVhc3VyZW1lbnRfaWQgPSBjYXNlX3doZW4oCiAgICAgIG5ldHdvcmsgPT0gInJlZXZhbHVhdGVkIiB+ICJEMmIiLAogICAgICBtZWFzdXJlbWVudF9pZCA9PSAxIH4gIkQxIiwKICAgICAgbWVhc3VyZW1lbnRfaWQgPT0gMiB+ICJEMWIiLAogICAgICBtZWFzdXJlbWVudF9pZCA9PSAzIH4gIkQyIgogICAgKSwKICAgIHNob3J0ZXN0X3BhdGggPSBmYWN0b3Ioc2hvcnRlc3RfcGF0aF9naXZlbl9vcHRzKQogICkgJT4lCiAgc2VsZWN0KAogICAgc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgsCiAgICBzdGFydHBvaW50X2lkLCBlbmRwb2ludF9pZCwKICAgIG9wdDFfaWQsIG9wdDJfaWQsCiAgICBjb3JyZWN0X2Nob2ljZSwgc3ViX2Nob2ljZSwKICAgIGNvcnJlY3QsIHJ0CiAgKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMiIpKQpgYGAKCmBgYHtyIHRpZXMtaHVtYW59CnRpZV9pdGVtX2FuYWx5c2lzX2h1bWFucyA8LSBiaW5kX3Jvd3MoCiAgbmF2X3N0dWR5MV90aWVzLCBuYXZfc3R1ZHkyX3RpZXMsIG5hdl9zdHVkeTNfdGllcwopICU+JQogIGZpbHRlcihtZWFzdXJlbWVudF9pZCA9PSAiRDEiKSAlPiUKICBncm91cF9ieShzaG9ydGVzdF9wYXRoLCBzdGFydHBvaW50X2lkLCBlbmRwb2ludF9pZCwgb3B0MV9pZCwgb3B0Ml9pZCkgJT4lCiAgc3VtbWFyaXNlKAogICAgcF9odW1hbl9vcHQxID0gbWVhbihzdWJfY2hvaWNlID09IG9wdDFfaWQpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKYGBgCgpgYGB7ciB0aWVzLWJmc30KYmZzX2F2Z19hY2N1cmFjeV90aWVzIDwtIGhlcmUoImRhdGEiLCAiYmZzLXNpbXMiLCAiYmZzX3NpbXNfbGVhcm5lZC5jc3YiKSAlPiUKICByZWFkX2NzdihzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUKICBmaWx0ZXIoCiAgICB0d29fY29ycmVjdF9vcHRpb25zID09IFRSVUUsCiAgICBzaG9ydGVzdF9wYXRoX2dpdmVuX29wdHMgPT0gc2hvcnRlc3RfcGF0aF9naXZlbl9zdGFydF9lbmQKICApICU+JQogIG11dGF0ZShzaG9ydGVzdF9wYXRoID0gZmFjdG9yKHNob3J0ZXN0X3BhdGhfZ2l2ZW5fb3B0cykpICU+JQogIHNlbGVjdCgKICAgIHNob3J0ZXN0X3BhdGgsIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLCBvcHQxX2lkLCBvcHQyX2lkLAogICAgYmZzX2Nob2ljZSwgYmZzX2NvcnJlY3RfY2hvaWNlLCBiZnNfbl92aXNpdHNfdG90YWwKICApICU+JQogIGdyb3VwX2J5KHNob3J0ZXN0X3BhdGgsIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLCBvcHQxX2lkLCBvcHQyX2lkKSAlPiUKICBzdW1tYXJpc2UoCiAgICBwX2Jmc19vcHQxID0gbWVhbihiZnNfY2hvaWNlID09IG9wdDFfaWQpLAogICAgYmZzX3Zpc2l0cyA9IG1lYW4oYmZzX25fdmlzaXRzX3RvdGFsKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgp0aWVfaXRlbV9hbmFseXNpc19iZnMgPC0gYmluZF9yb3dzKAogIG5hdl9zdHVkeTFfdGllcywgbmF2X3N0dWR5Ml90aWVzLCBuYXZfc3R1ZHkzX3RpZXMKKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgPT0gIkQxIikgJT4lCiAgbGVmdF9qb2luKAogICAgcGFyYW1zICU+JQogICAgICBmaWx0ZXIobW9kZWwgPT0gImJmcyIpICU+JQogICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcGFyYW1fbmFtZSwgdmFsdWVzX2Zyb20gPSBwYXJhbV92YWx1ZSkgJT4lCiAgICAgIHNlbGVjdChzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgc2VhcmNoX3RocmVzaG9sZCwgbGFwc2VfcmF0ZSkKICApICU+JQogIGxlZnRfam9pbihiZnNfYXZnX2FjY3VyYWN5X3RpZXMpICU+JQogIG11dGF0ZSgKICAgIHBfc3ViX2Nob2ljZV9iZnMgPSBpZl9lbHNlKAogICAgICBzdWJfY2hvaWNlID09IG9wdDFfaWQsCiAgICAgIHBfYmZzX29wdDEsCiAgICAgIDEgLSBwX2Jmc19vcHQxCiAgICApCiAgKSAlPiUKICBzZWxlY3QoLXBfYmZzX29wdDEpICU+JQogICMgV2hhdCdzIHRoZSBwcm9iYWJpbGl0eSBvZiAqY29tcGxldGluZyogQkZTLW9ubGluZSBhbGwgdGhlIHdheSB0aHJvdWdoPwogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoCiAgICBzZWFyY2hfdGhyZXNob2xkID0gc2VhcmNoX3RocmVzaG9sZCwKICAgIHBfY29tcGxldGVfYmZzID0gc29mdG1heCgKICAgICAgb3B0aW9uX3ZhbHVlcyA9IGMoc2VhcmNoX3RocmVzaG9sZCwgYmZzX3Zpc2l0cyksCiAgICAgIG9wdGlvbl9jaG9zZW4gPSAxLAogICAgICB0ZW1wZXJhdHVyZSA9IDEKICAgICkKICApICU+JQogIHVuZ3JvdXAoKSAlPiUKICAjIFdlaWdoIEJGUyBwcmVkaWN0aW9ucyBhY2NvcmRpbmdseQogIG11dGF0ZSgKICAgIHBfZ2l2ZV91cCA9IDEgLSBwX2NvbXBsZXRlX2JmcywKICAgIHBfYmZzX29wdDEgPSAocF9jb21wbGV0ZV9iZnMgKiBwX3N1Yl9jaG9pY2VfYmZzKSArIChwX2dpdmVfdXAgKiAxLzIpLAogICAgIyMjIEFkZCBsYXBzZSByYXRlCiAgICAjICAgRGl2aWRpbmcgYnkgMiBpcyBiZWNhdXNlIHRoZXJlIGFyZSB0d28gb3B0aW9ucyB0byBjaG9vc2UgZnJvbQogICAgIyAgIFRoZXJlZm9yZSwgd2hlbiBsYXBzZSByYXRlID0gMSwgdGhpcyBiZWNvbWVzIGNoYW5jZSA9IDEvMgogICAgcF9iZnNfb3B0MSA9IHBfYmZzX29wdDEgKiAoMS1sYXBzZV9yYXRlKSArIChsYXBzZV9yYXRlLzIpCiAgKSAlPiUKICAjIEF2ZXJhZ2Ugb3ZlciB0cmlhbHMKICAjIGdyb3VwX2J5KHNob3J0ZXN0X3BhdGgpICU+JQogIGdyb3VwX2J5KHNob3J0ZXN0X3BhdGgsIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLCBvcHQxX2lkLCBvcHQyX2lkKSAlPiUKICBzdW1tYXJpc2UoCiAgICBwX2Jmc19vcHQxID0gbWVhbihwX2Jmc19vcHQxKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCmBgYAoKYGBge3IgdGllcy1zcn0KdGllX2l0ZW1fYW5hbHlzaXNfc3IgPC0gYmluZF9yb3dzKAogIG5hdl9zdHVkeTFfdGllcywgbmF2X3N0dWR5Ml90aWVzLCBuYXZfc3R1ZHkzX3RpZXMKKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgPT0gIkQxIikgJT4lCiAgbGVmdF9qb2luKAogICAgcHBjX3NyX21hdHJpeCAlPiUKICAgICAgc2VsZWN0KAogICAgICAgIHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkLAogICAgICAgIG9wdDFfaWQgPSBmcm9tLAogICAgICAgIGVuZHBvaW50X2lkID0gdG8sCiAgICAgICAgb3B0MV9zciA9IHNyX3ZhbHVlCiAgICAgICkKICApICU+JQogIGxlZnRfam9pbigKICAgIHBwY19zcl9tYXRyaXggJT4lCiAgICAgIHNlbGVjdCgKICAgICAgICBzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwKICAgICAgICBvcHQyX2lkID0gZnJvbSwKICAgICAgICBlbmRwb2ludF9pZCA9IHRvLAogICAgICAgIG9wdDJfc3IgPSBzcl92YWx1ZSwKICAgICAgICBzcl9nYW1tYSwgc29mdG1heF90ZW1wZXJhdHVyZSwgbGFwc2VfcmF0ZQogICAgICApCiAgKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKAogICAgcF9zcl9vcHQxID0gc29mdG1heCgKICAgICAgb3B0aW9uX3ZhbHVlcyA9IGMob3B0MV9zciwgb3B0Ml9zcikgKiAxMDAsCiAgICAgIG9wdGlvbl9jaG9zZW4gPSAxLAogICAgICB0ZW1wZXJhdHVyZSA9IHNvZnRtYXhfdGVtcGVyYXR1cmUsCiAgICAgIHVzZV9pbnZlcnNlX3RlbXBlcmF0dXJlID0gVFJVRSwKICAgICAgbGFwc2VfcmF0ZSA9IGxhcHNlX3JhdGUKICAgICkKICApICU+JQogIGdyb3VwX2J5KHNob3J0ZXN0X3BhdGgsIHN0YXJ0cG9pbnRfaWQsIGVuZHBvaW50X2lkLCBvcHQxX2lkLCBvcHQyX2lkKSAlPiUKICBzdW1tYXJpc2UoCiAgICBwX3NyX29wdDEgPSBtZWFuKHBfc3Jfb3B0MSwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCmBgYAoKYGBge3IgcGxvdC10aWVzfQpwbG90X3RpZXMgPC0gdGllX2l0ZW1fYW5hbHlzaXNfaHVtYW5zICU+JQogIGxlZnRfam9pbih0aWVfaXRlbV9hbmFseXNpc19iZnMpICU+JQogIGxlZnRfam9pbih0aWVfaXRlbV9hbmFseXNpc19zcikgJT4lCiAgbXV0YXRlKGl0ZW0gPSByb3dfbnVtYmVyKCksIGl0ZW0gPSBmYWN0b3IoaXRlbSkpICU+JQogIHBpdm90X2xvbmdlcigKICAgIHN0YXJ0c193aXRoKCJwXyIpLAogICAgbmFtZXNfdG8gPSAiYWdlbnQiLAogICAgdmFsdWVzX3RvID0gInBfY2hvb3NlX29wdDEiCiAgKSAlPiUKICBtdXRhdGUoCiAgICBhZ2VudCA9IHN0cl9yZW1vdmVfYWxsKGFnZW50LCAicF98X29wdDEiKSwKICAgIGFnZW50ID0gY2FzZV93aGVuKAogICAgICBhZ2VudCA9PSAiaHVtYW4iIH4gIkh1bWFuIiwKICAgICAgYWdlbnQgPT0gInNyIiB+ICJTUiIsCiAgICAgIGFnZW50ID09ICJiZnMiIH4gIkJGUyIsCiAgICApLAogICAgYWdlbnQgPSBmY3RfcmVsZXZlbChhZ2VudCwgIkh1bWFuIiwgIlNSIikKICApICU+JQogIGdncGxvdChhZXMoeD1zaG9ydGVzdF9wYXRoLCB5PXBfY2hvb3NlX29wdDEpKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIGZhY2V0X3dyYXAofmFnZW50KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMjUpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJjcm9zc2JhciIsIGZ1biA9IG1lYW4sIGNvbG9yID0gInJlZCIpICsKICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiU2hvcnRlc3QgcGF0aCBkaXN0YW5jZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gInAoQ2hvb3NlIFNvdXJjZSBBID4gQikiLAogICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBicmVha3MgPSBzZXEoMCwgMSwgMC4yNSkKICApICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIgogICkgKwogIGdndGl0bGUoIk5hdmlnYXRpb24gcHJvYmxlbXMgd2l0aCB0d28gY29ycmVjdCBhbnN3ZXJzIikKCnBsb3RfdGllcwoKaWYgKGtuaXR0aW5nKSB7CiAgZ2dzYXZlKAogICAgaGVyZSgiZmlndXJlcyIsIHN0cl9jKCJuYXZpZ2F0aW9uX2hlbGRvdXRfZGF5MSIsICIucGRmIikpLAogICAgcGxvdCA9IHBsb3RfdGllcywKICAgIHdpZHRoID0gOC8yLCBoZWlnaHQgPSAyLjUsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCgojIERvZXMgbmF2aWdhdGlvbiBpbXByb3ZlIGFmdGVyIHJlc3Q/CgpGaXJzdCwgc29tZSBkZXNjcmlwdGl2ZXMuLi4KCmBgYHtyIGRlc2NyaXB0aXZlLW5hdmlnYXRpb24tYWxsfQpiaW5kX3Jvd3MobmF2X3N0dWR5MSwgbmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZ3JvdXBfYnkobWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgpICU+JQogIHN1bW1hcmlzZShhY2N1cmFjeSA9IG1lYW4oY29ycmVjdCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIGFycmFuZ2UobWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgpICU+JQogIHBpdm90X3dpZGVyKAogICAgbmFtZXNfZnJvbSA9IHNob3J0ZXN0X3BhdGgsIHZhbHVlc19mcm9tID0gYWNjdXJhY3ksIG5hbWVzX3ByZWZpeCA9ICJkaXN0LSIKICApICU+JQogIGtibCgKICAgIGNhcHRpb24gPSBzdHJfYygKICAgICAgIjxjZW50ZXI+IiwgIkRlc2NyaXB0aXZlOiBOYXZpZ2F0aW9uIGFjY3VyYWN5IiwgIjwvY2VudGVyPiIKICAgICksCiAgICBkaWdpdHMgPSAyCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygicmVzcG9uc2l2ZSIpKQpgYGAKCiMjIE92ZXJuaWdodCByZXN0CgpOb3cgd2Ugd2FudCB0byBzZWUgaG93IG5hdmlnYXRpb24gYWNjdXJhY3kgY2hhbmdlcyBmcm9tIGRheSAxIHRvIGRheSAyLiBUaGlzIHBvb2xzIGFjcm9zcyBzdHVkaWVzIDItMyAoc3R1ZHkgMSB3YXMgb25seSBhIG9uZS1kYXkgZXhwZXJpbWVudCkuCgpgYGB7ciBzdGF0cy1kYXkxLXRvLWRheTJ9Cm5hdl9kYXkxX3RvX2RheTIgPC0gYmluZF9yb3dzKG5hdl9zdHVkeTIsIG5hdl9zdHVkeTMpICU+JQogIGZpbHRlcihtZWFzdXJlbWVudF9pZCAlaW4lIGMoIkQxIiwgIkQyIikpICU+JQogICMgR2l2ZSBldmVyeSBzdWJqZWN0IGEgZGlzdGluY3QgaWRlbnRpZmllcgogIG11dGF0ZShzdWJfaWQgPSBzdHJfYyhzdHVkeSwgIiBzIiwgc3ViX2lkKSkKCnN0YXRzX25hdl9kYXkyX2Rpc3QyIDwtIG5hdl9kYXkxX3RvX2RheTIgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMiIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSArICgxIHwgc3R1ZHkpLAogICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICBkYXRhID0gLgogICkKCnN0YXRzX25hdl9kYXkyX2Rpc3QzIDwtIG5hdl9kYXkxX3RvX2RheTIgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMyIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSArICgxIHwgc3R1ZHkpLAogICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICBkYXRhID0gLgogICkKCnN0YXRzX25hdl9kYXkyX2Rpc3Q0IDwtIG5hdl9kYXkxX3RvX2RheTIgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiNCIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSArICgxIHwgc3R1ZHkpLAogICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICBkYXRhID0gLgogICkKCm1hcF9kZnIoCiAgLnggPSBsaXN0KAogICAgImRpc3QtMiIgPSBzdGF0c19uYXZfZGF5Ml9kaXN0MiwKICAgICJkaXN0LTMiID0gc3RhdHNfbmF2X2RheTJfZGlzdDMsCiAgICAiZGlzdC00IiA9IHN0YXRzX25hdl9kYXkyX2Rpc3Q0CiAgKSwKICAuZiA9IH50aWR5KC54LCBjb25mLmludCA9IFRSVUUpLAogIC5pZCA9ICJyZWZfY2F0IgopICU+JQogIGNoZWNrX3NpZ25pZmljYW5jZSgpICU+JQogIHNlbGVjdCgtYyhyZWZfY2F0LCBlZmZlY3QsIGNvbXBvbmVudCkpICU+JQogIGtibCgKICAgIGNhcHRpb24gPSBzdHJfYygKICAgICAgIjxjZW50ZXI+IiwgIk5hdmlnYXRpb24gYWNjdXJhY3k6IERheSAxIHRvIERheSAyIiwgIjwvY2VudGVyPiIKICAgICksCiAgICBkaWdpdHMgPSAzCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygicmVzcG9uc2l2ZSIpKSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTIiLCAxLCAxNykgJT4lCiAgcGFja19yb3dzKCJSZWYuIENhdC4gZGlzdC0zIiwgMTgsIDM0KSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTQiLCAzNSwgNTEpCmBgYAoKYGBge3IgcGxvdC1kYXkxLXRvLWRheTJ9CnByZWRpY3RfbmF2X2RheTFfdG9fZGF5MiA8LSBleHBhbmRfZ3JpZCgKICBtZWFzdXJlbWVudF9pZCA9IGMoIkQxIiwgIkQyIiksCiAgc2hvcnRlc3RfcGF0aCA9IGZhY3RvcigyOjQpLAogIHN1Yl9pZCA9IE5BLCBzdHVkeSA9IE5BCikgJT4lCiAgcHJlZGljdF9nbG1tVE1CKHN0YXRzX25hdl9kYXkyX2Rpc3QyKQoKcGxvdF9uYXZfZGF5MV90b19kYXkyIDwtIG5hdl9kYXkxX3RvX2RheTIgJT4lCiAgZ3JvdXBfYnkoc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgc2hvcnRlc3RfcGF0aCkgJT4lCiAgc3VtbWFyaXNlKGFjY3VyYWN5ID0gbWVhbihjb3JyZWN0KSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgZ2dwbG90KGFlcyh4PXNob3J0ZXN0X3BhdGgsIHk9YWNjdXJhY3ksIGNvbG9yPW1lYXN1cmVtZW50X2lkKSkgKwogIHRoZW1lX2N1c3RvbSgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2RvdHBsb3QoCiAgICBhZXMoZmlsbCA9IG1lYXN1cmVtZW50X2lkKSwKICAgIGJpbndpZHRoID0gMC4wMSwKICAgIGJpbmF4aXMgPSAieSIsIHN0YWNrZGlyID0gImNlbnRlciIsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43NSksCiAgICBkb3RzaXplID0gMSwgYWxwaGEgPSAwLjUsIGNvbG9yID0gTkEsCiAgICBzaG93LmxlZ2VuZCA9IEZBTFNFCiAgKSArCiAgZ2VvbV9wb2ludHJhbmdlKAogICAgYWVzKAogICAgICB4ID0gc2hvcnRlc3RfcGF0aCwgeSA9IGZpdCwKICAgICAgeW1pbiA9IGZpdCAtIHNlLmZpdCwgeW1heCA9IGZpdCArIHNlLmZpdCwKICAgICAgY29sb3IgPSBtZWFzdXJlbWVudF9pZAogICAgKSwKICAgIGRhdGEgPSBwcmVkaWN0X25hdl9kYXkxX3RvX2RheTIsIGluaGVyaXQuYWVzID0gRkFMU0UsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4xNSksIGxpbmV3aWR0aCA9IDEKICApICsKICBnZW9tX2xpbmUoCiAgICBhZXMoCiAgICAgIHggPSBzaG9ydGVzdF9wYXRoLCB5ID0gZml0LAogICAgICBncm91cCA9IG1lYXN1cmVtZW50X2lkLCBjb2xvciA9IG1lYXN1cmVtZW50X2lkCiAgICApLAogICAgZGF0YSA9IHByZWRpY3RfbmF2X2RheTFfdG9fZGF5MiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjE1KSwgbGluZXdpZHRoID0gMQogICkgKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJTaG9ydGVzdCBwYXRoIGRpc3RhbmNlIikgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiQWNjdXJhY3kiLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjI1KQogICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIG5hbWUgPSAiTWVhc3VyZW1lbnQiLAogICAgdmFsdWVzID0gYygiRDEiPSIjZmE5ZmI1IiwgIkQyIj0iIzdhMDE3NyIpLAogICAgbGFiZWxzID0gYygiRDEiPSJCZWZvcmUgb3Zlcm5pZ2h0IHJlc3QiLCAiRDIiPSJBZnRlciBvdmVybmlnaHQgcmVzdCIpCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiRDEiPSIjZmE5ZmI1IiwgIkQyIj0iIzdhMDE3NyIpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAuMywgMS4xKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ2d0aXRsZSgiTmF2aWdhdGlvbiBhZnRlciBvdmVybmlnaHQgcmVzdCIpCgpwbG90X25hdl9kYXkxX3RvX2RheTIKCmlmIChrbml0dGluZykgewogIGdnc2F2ZSgKICAgIGhlcmUoImZpZ3VyZXMiLCBzdHJfYygibmF2aWdhdGlvbl9kYXkxX3RvX2RheTIiLCAiLnBkZiIpKSwKICAgIHBsb3QgPSBwbG90X25hdl9kYXkxX3RvX2RheTIsCiAgICB3aWR0aCA9IDQsIGhlaWdodCA9IDMsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCiMjIEF3YWtlIHJlc3QKCklzIGEgYnJpZWYgcGVyaW9kIG9mIGF3YWtlIHJlc3Qgc3VmZmljaWVudCBmb3IgaW1wcm92aW5nIG5hdmlnYXRpb24gYWNjdXJhY3k/CgpgYGB7ciBzdGF0cy1hd2FrZS1yZXN0fQpzdGF0c19uYXZfYXdha2VfZGlzdDIgPC0gbmF2X3N0dWR5MyAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMWIiKSkgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMiIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSwKICAgIGZhbWlseSA9IGJpbm9taWFsLAogICAgZGF0YSA9IC4KICApCgpzdGF0c19uYXZfYXdha2VfZGlzdDMgPC0gbmF2X3N0dWR5MyAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMWIiKSkgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMyIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSwKICAgIGZhbWlseSA9IGJpbm9taWFsLAogICAgZGF0YSA9IC4KICApCgpzdGF0c19uYXZfYXdha2VfZGlzdDQgPC0gbmF2X3N0dWR5MyAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMWIiKSkgJT4lCiAgbXV0YXRlKHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiNCIpKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSwKICAgIGZhbWlseSA9IGJpbm9taWFsLAogICAgZGF0YSA9IC4KICApCgptYXBfZGZyKAogIC54ID0gbGlzdCgKICAgICJkaXN0LTIiID0gc3RhdHNfbmF2X2F3YWtlX2Rpc3QyLAogICAgImRpc3QtMyIgPSBzdGF0c19uYXZfYXdha2VfZGlzdDMsCiAgICAiZGlzdC00IiA9IHN0YXRzX25hdl9hd2FrZV9kaXN0NAogICksCiAgLmYgPSB+dGlkeSgueCwgY29uZi5pbnQgPSBUUlVFKSwKICAuaWQgPSAicmVmX2NhdCIKKSAlPiUKICBjaGVja19zaWduaWZpY2FuY2UoKSAlPiUKICBzZWxlY3QoLWMocmVmX2NhdCwgZWZmZWN0LCBjb21wb25lbnQpKSAlPiUKICBrYmwoCiAgICBjYXB0aW9uID0gc3RyX2MoIjxjZW50ZXI+IiwgIk5hdmlnYXRpb24gYWNjdXJhY3k6IEF3YWtlIFJlc3QiLCAiPC9jZW50ZXI+IiksCiAgICBkaWdpdHMgPSAzCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygicmVzcG9uc2l2ZSIpKSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTIiLCAxLCAxNikgJT4lCiAgcGFja19yb3dzKCJSZWYuIENhdC4gZGlzdC0zIiwgMTcsIDMyKSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTQiLCAzMywgNDgpCmBgYAoKYGBge3IgcGxvdC1hd2FrZS1yZXN0fQpwcmVkaWN0X25hdl9hd2FrZSA8LSBleHBhbmRfZ3JpZCgKICBtZWFzdXJlbWVudF9pZCA9IGMoIkQxIiwgIkQxYiIpLAogIHNob3J0ZXN0X3BhdGggPSBmYWN0b3IoMjo0KSwKICBzdWJfaWQgPSBOQSwgc3R1ZHkgPSBOQQopICU+JQogIHByZWRpY3RfZ2xtbVRNQihzdGF0c19uYXZfYXdha2VfZGlzdDIpCgpwbG90X25hdl9hd2FrZSA8LSBuYXZfc3R1ZHkzICU+JQogIGZpbHRlcihtZWFzdXJlbWVudF9pZCAlaW4lIGMoIkQxIiwgIkQxYiIpKSAlPiUKICBncm91cF9ieShzdWJfaWQsIG1lYXN1cmVtZW50X2lkLCBzaG9ydGVzdF9wYXRoKSAlPiUKICBzdW1tYXJpc2UoYWNjdXJhY3kgPSBtZWFuKGNvcnJlY3QpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c2hvcnRlc3RfcGF0aCwgeT1hY2N1cmFjeSwgY29sb3I9bWVhc3VyZW1lbnRfaWQpKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fZG90cGxvdCgKICAgIGFlcyhmaWxsID0gbWVhc3VyZW1lbnRfaWQpLAogICAgYmlud2lkdGggPSAwLjAxLAogICAgYmluYXhpcyA9ICJ5Iiwgc3RhY2tkaXIgPSAiY2VudGVyIiwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjc1KSwKICAgIGRvdHNpemUgPSAxLCBhbHBoYSA9IDAuNSwgY29sb3IgPSBOQSwKICAgIHNob3cubGVnZW5kID0gRkFMU0UKICApICsKICBnZW9tX3BvaW50cmFuZ2UoCiAgICBhZXMoCiAgICAgIHggPSBzaG9ydGVzdF9wYXRoLCB5ID0gZml0LAogICAgICB5bWluID0gZml0IC0gc2UuZml0LCB5bWF4ID0gZml0ICsgc2UuZml0LAogICAgICBjb2xvciA9IG1lYXN1cmVtZW50X2lkCiAgICApLAogICAgZGF0YSA9IHByZWRpY3RfbmF2X2F3YWtlLCBpbmhlcml0LmFlcyA9IEZBTFNFLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuMTUpLCBsaW5ld2lkdGggPSAxCiAgKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKAogICAgICB4ID0gc2hvcnRlc3RfcGF0aCwgeSA9IGZpdCwKICAgICAgZ3JvdXAgPSBtZWFzdXJlbWVudF9pZCwgY29sb3IgPSBtZWFzdXJlbWVudF9pZAogICAgKSwKICAgIGRhdGEgPSBwcmVkaWN0X25hdl9hd2FrZSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjE1KSwgbGluZXdpZHRoID0gMQogICkgKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJTaG9ydGVzdCBwYXRoIGRpc3RhbmNlIikgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiQWNjdXJhY3kiLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjI1KQogICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIG5hbWUgPSAiTWVhc3VyZW1lbnQiLAogICAgdmFsdWVzID0gYygiRDEiPSIjZmE5ZmI1IiwgIkQxYiI9IiMyYzdmYjgiKSwKICAgIGxhYmVscyA9IGMoIkQxIj0iQmVmb3JlIG92ZXJuaWdodCByZXN0IiwgIkQxYiI9IkFmdGVyIGF3YWtlIHJlc3QiKQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkQxIj0iI2ZhOWZiNSIsICJEMWIiPSIjMmM3ZmI4IikpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMC4zLCAxLjEpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZ3RpdGxlKCJOYXZpZ2F0aW9uIGFmdGVyIGF3YWtlIHJlc3QiKQoKcGxvdF9uYXZfYXdha2UKCmlmIChrbml0dGluZykgewogIGdnc2F2ZSgKICAgIGhlcmUoImZpZ3VyZXMiLCBzdHJfYygibmF2aWdhdGlvbl9hd2FrZV9yZXN0IiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9uYXZfYXdha2UsCiAgICB3aWR0aCA9IDQsIGhlaWdodCA9IDMsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCiMjIFNSIHJlcGxheSBzaW11bGF0aW9uCgpCZWZvcmUgc3RhcnRpbmcgdG8gZG8gYW55IHNpbXVsYXRpb24sIHdlIGZpcnN0IHdhbnQgdG8ga25vdyBob3cgbXVjaCByZXBsYXkgYW4gYWdlbnQgY2FuIGZpdCBpbnRvIGRpZmZlcmVudCBwZXJpb2RzIG9mIHRpbWUuIEJhc2VkIG9uIHBhc3QgcmVzZWFyY2ggbWVhc3VyaW5nIG5ldXJhbCByZXBsYXkgZXZlbnRzLCB3ZSdsbCBhc3N1bWUgdGhhdCBpdCB0YWtlcyBhYm91dCA1MG1zIGZvciB0aGUgYnJhaW4gdG8gcmVwbGF5IGEgc2luZ2xlIGl0ZW0gZnJvbSBhIHNlcXVlbmNlLiBUaGlzIHdpbGwgbGV0IHVzIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIHRoYXQgY291bGQgYmUgcmVwbGF5ZWQgaW4gZWFjaCBlcG9jaC4gVG8gc2ltcGxpZnkgdGhlIHByb2Nlc3Mgb2YgYWN0dWFsbHkgcnVubmluZyB0aGUgc2ltdWxhdGlvbiwgd2UnbGwgY29udmVydCB0aGlzIHF1YW50aXR5IGludG8gdGhlIG51bWJlciBvZiAic2V0cyIgdGhhdCBjYW4gYmUgcmVwbGF5ZWQsIHdoZXJlIGEgc2luZ2xlICJzZXQiIGNvbnNpc3RzIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgcmVsYXRpb25zaGlwcyBpbiB0aGUgbmV0d29yayAoaS5lLiwgdGhlIG51bWJlciBvZiB1bmRpcmVjdGVkIGVkZ2VzID0gMTcsIG1lYW5pbmcgdGhhdCBlYWNoIHNldCBjb25zaXN0cyBvZiAzNCBvYnNlcnZhdGlvbnMgYmVjYXVzZSBlYWNoIHNldCBjb250YWlucyBvYnNlcnZhdGlvbnMgb2YgYm90aCBBLT5CIGFuZCBCLT5BKS4KCmBgYHtyIGF2YWlsLXNyLXJlcGxheS10aW1lfQp0aWJibGUobWludXRlc19mb3JfcmVwbGF5ID0gYygxLCA1LCAxNSwgMzAsIDYwLCAxMjApKSAlPiUKICBtdXRhdGUoCiAgICBuX2l0ZW1zID0gbWludXRlc19mb3JfcmVwbGF5IC8gKDAuMDUgLyA2MCksCiAgICBuX3NldHMgPSBuX2l0ZW1zIC8gMzQKICApICU+JQogIGtibCgKICAgIGNhcHRpb24gPSBzdHJfYygiPGNlbnRlcj4iLCAiU1IgcmVwbGF5IHRpbWUiLCAiPC9jZW50ZXI+IiksCiAgICBkaWdpdHMgPSAyCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygicmVzcG9uc2l2ZSIpKQpgYGAKCmBgYHtyIHNpbXVsYXRlLXNyLXJlcGxheX0Kc2ltX3JlcGxheV8wX21pbiA8LSBzaW11bGF0ZWRfcGFpcmVkX2Fzc29jaWF0ZXMgJT4lCiAgZmlsdGVyKHNldCA8PSA2KSAlPiUKICBzaW11bGF0ZV9zcigpICU+JQogIG11dGF0ZShyZXBsYXlfdGltZSA9IDApCgpzaW1fcmVwbGF5XzFfbWluIDwtIHNpbXVsYXRlZF9wYWlyZWRfYXNzb2NpYXRlcyAlPiUKICBmaWx0ZXIoc2V0IDw9IDM1ICsgNikgJT4lCiAgc2ltdWxhdGVfc3IoKSAlPiUKICBtdXRhdGUocmVwbGF5X3RpbWUgPSAxKQoKc2ltX3JlcGxheV81X21pbiA8LSBzaW11bGF0ZWRfcGFpcmVkX2Fzc29jaWF0ZXMgJT4lCiAgZmlsdGVyKHNldCA8PSAxNzYgKyA2KSAlPiUKICBzaW11bGF0ZV9zcigpICU+JQogIG11dGF0ZShyZXBsYXlfdGltZSA9IDUpCgpzaW1fcmVwbGF5XzYwX21pbiA8LSBzaW11bGF0ZWRfcGFpcmVkX2Fzc29jaWF0ZXMgJT4lCiAgZmlsdGVyKHNldCA8PSAyMTE3ICsgNikgJT4lCiAgc2ltdWxhdGVfc3IoKSAlPiUKICBtdXRhdGUocmVwbGF5X3RpbWUgPSA2MCkKYGBgCgpgYGB7ciBwbG90LXNyLXNpbS1yZXBsYXl9CnBsb3Rfc2ltX3NyX3JlcGxheSA8LSBiaW5kX3Jvd3MoCiAgam9pbl9zcih0cmlhbGxpc3RfbmF2X2xlYXJuZWQsIHNpbV9yZXBsYXlfMF9taW4pLAogIGpvaW5fc3IodHJpYWxsaXN0X25hdl9sZWFybmVkLCBzaW1fcmVwbGF5XzFfbWluKSwKICBqb2luX3NyKHRyaWFsbGlzdF9uYXZfbGVhcm5lZCwgc2ltX3JlcGxheV81X21pbiksCiAgam9pbl9zcih0cmlhbGxpc3RfbmF2X2xlYXJuZWQsIHNpbV9yZXBsYXlfNjBfbWluKQopICU+JQogICMgRmVlZCB2YWx1ZXMgdGhyb3VnaCBzb2Z0bWF4CiAgbXV0YXRlKGFjcm9zcyhjKG9wdDFfc3IsIG9wdDJfc3IpLCB+LnggKiAxMDApKSAlPiUKICBleHBhbmRfZ3JpZCh0ZW1wZXJhdHVyZSA9IDEpICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoCiAgICBwX2NvcnJlY3QgPSBzb2Z0bWF4KAogICAgICBvcHRpb25fdmFsdWVzID0gYyhvcHQxX3NyLCBvcHQyX3NyKSwKICAgICAgb3B0aW9uX2Nob3NlbiA9IGlmX2Vsc2UoY29ycmVjdF9jaG9pY2UgPT0gb3B0MV9pZCwgMSwgMiksCiAgICAgIHRlbXBlcmF0dXJlID0gdGVtcGVyYXR1cmUsCiAgICAgIHVzZV9pbnZlcnNlX3RlbXBlcmF0dXJlID0gVFJVRQogICAgKQogICkgJT4lCiAgdW5ncm91cCgpICU+JQogICMgRm9yIHBsb3R0aW5nCiAgbXV0YXRlKAogICAgZ2FtbWEgPSBmYWN0b3IoZ2FtbWEpLAogICAgdGVtcGVyYXR1cmUgPSBzdHJfcGFkKHRlbXBlcmF0dXJlLCB3aWR0aCA9IDIsIHNpZGUgPSAibGVmdCIpLAogICAgdGVtcGVyYXR1cmUgPSBzdHJfYyh1bmljb2RlX2dyZWVrWyJ0YXUiXSwgIiA9ICIsIHRlbXBlcmF0dXJlKSwKICAgIHJlcGxheV90aW1lID0gc3RyX3BhZChyZXBsYXlfdGltZSwgd2lkdGggPSAyLCBzaWRlID0gImxlZnQiKSwKICAgIHJlcGxheV90aW1lID0gY2FzZV93aGVuKAogICAgICBzdHJfZGV0ZWN0KHJlcGxheV90aW1lLCAiMSQiKSB+IHN0cl9jKHJlcGxheV90aW1lLCAiIG1pbnV0ZSBvZiByZXBsYXkiKSwKICAgICAgcmVwbGF5X3RpbWUgPT0gIiAwIiB+ICJObyByZXBsYXkiLAogICAgICBUUlVFIH4gc3RyX2MocmVwbGF5X3RpbWUsICIgbWludXRlcyBvZiByZXBsYXkiKQogICAgKSwKICAgIHJlcGxheV90aW1lID0gZmN0X3JlbGV2ZWwocmVwbGF5X3RpbWUsICJObyByZXBsYXkiKQogICkgJT4lCiAgZ3JvdXBfYnkocmVwbGF5X3RpbWUsIGdhbW1hLCB0ZW1wZXJhdHVyZSwgc2hvcnRlc3RfcGF0aCkgJT4lCiAgc3VtbWFyaXNlKHBfY29ycmVjdCA9IG1lYW4ocF9jb3JyZWN0KSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgIyBQbG90CiAgZ2dwbG90KGFlcyh4PXNob3J0ZXN0X3BhdGgsIHk9cF9jb3JyZWN0LCBjb2xvcj1nYW1tYSkpICsKICB0aGVtZV9jdXN0b20oKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhyZXBsYXlfdGltZSksIHJvd3MgPSB2YXJzKHRlbXBlcmF0dXJlKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fbGluZShhZXMoY29sb3IgPSBnYW1tYSwgZ3JvdXAgPSBnYW1tYSksIGxpbmV3aWR0aCA9IDAuOCkgKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJTaG9ydGVzdCBwYXRoIGRpc3RhbmNlIikgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkFjY3VyYWN5IiwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKAogICAgbmFtZSA9IHN0cl9jKHVuaWNvZGVfZ3JlZWtbImdhbW1hIl0sICIgPSAiKSwgb3B0aW9uID0gInR1cmJvIiwgZW5kID0gMC45CiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKGJ5cm93ID0gVFJVRSwgbnJvdyA9IDEpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAuNSwgMSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGdndGl0bGUoIlNpbXVsYXRlZCBlZmZlY3RzIG9mIFNSIHJlcGxheSBvbiBuYXZpZ2F0aW9uIikKCnBsb3Rfc2ltX3NyX3JlcGxheQoKaWYgKGtuaXR0aW5nKSB7CiAgZ2dzYXZlKAogICAgaGVyZSgiZmlndXJlcyIsIHN0cl9jKCJzaW11bGF0ZWRfcmVwbGF5IiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9zaW1fc3JfcmVwbGF5LAogICAgd2lkdGggPSA3LCBoZWlnaHQgPSAyLjUsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMCwKICAgIGRldmljZSA9IGNhaXJvX3BkZgogICkKfQpgYGAKCiMjIFBQQyBiZWZvcmUvYWZ0ZXIgcmVzdAoKYGBge3IgcGxvdC1wcGMtZGF5MS1kYXkyfQpwbG90X3BwY19kYXkxX3RvX2RheTIgPC0gcHBjX3NyX2JlaGF2aW9yICU+JQogIGZpbHRlcihzdHVkeSAlaW4lIGMoIlN0dWR5IDIiLCAiU3R1ZHkgMyIpKSAlPiUKICBtdXRhdGUoCiAgICBtZWFzdXJlbWVudF9pZCA9IGNhc2Vfd2hlbigKICAgICAgbWVhc3VyZW1lbnRfaWQgPT0gIkQxIiB+ICJCZWZvcmUgcmVzdCIsCiAgICAgIG1lYXN1cmVtZW50X2lkID09ICJEMiIgfiAiQWZ0ZXIgcmVzdCIKICAgICksCiAgICBtZWFzdXJlbWVudF9pZCA9IGZjdF9yZWxldmVsKG1lYXN1cmVtZW50X2lkLCAiQmVmb3JlIHJlc3QiKQogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKGMoZW1waXJpY2FsLCBwcmVkaWN0ZWQpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c2hvcnRlc3RfcGF0aCwgeT12YWx1ZSkpICsKICB0aGVtZV9jdXN0b20oKSArCiAgZmFjZXRfd3JhcCh+bWVhc3VyZW1lbnRfaWQpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3BvaW50KAogICAgYWVzKGNvbG9yID0gbmFtZSksCiAgICBhbHBoYSA9IDAuMDUsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKAogICAgICBqaXR0ZXIud2lkdGggPSAwLjIsIGppdHRlci5oZWlnaHQgPSAwLCBkb2RnZS53aWR0aCA9IDAuNSwgc2VlZCA9IDEKICAgICksCiAgICBzaG93LmxlZ2VuZCA9IEZBTFNFCiAgKSArCiAgc3RhdF9zdW1tYXJ5KAogICAgYWVzKGNvbG9yID0gbmFtZSksIGdlb20gPSAiY3Jvc3NiYXIiLCBmdW4gPSBtZWFuLAogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpCiAgKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIlNob3J0ZXN0IHBhdGggZGlzdGFuY2UiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9ICJBY2N1cmFjeSIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgYnJlYWtzID0gc2VxKDAsIDEsIDAuMjUpCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgbmFtZSA9IE5VTEwsCiAgICB2YWx1ZXMgPSBjKCJlbXBpcmljYWwiPSIjZmQ4ZDNjIiwgInByZWRpY3RlZCI9IiNiZDAwMjYiKSwKICAgIGxhYmVscyA9IGMoImVtcGlyaWNhbCI9Ikh1bWFuIiwgInByZWRpY3RlZCI9IlNSIikKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGdndGl0bGUoIlBvc3RlcmlvciBwcmVkaWN0aXZlIGNoZWNrIikKCnBsb3RfcHBjX2RheTFfdG9fZGF5MgoKaWYgKGtuaXR0aW5nKSB7CiAgZ2dzYXZlKAogICAgaGVyZSgiZmlndXJlcyIsIHN0cl9jKCJwcGNfZGF5MiIsICIucGRmIikpLAogICAgcGxvdCA9IHBsb3RfcHBjX2RheTFfdG9fZGF5MiwKICAgIHdpZHRoID0gOC8zLCBoZWlnaHQgPSAyLjUsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCgojIyBSZWxhdGluZyBtb2RlbCBwYXJhbWV0ZXJzIHRvIG5hdmlnYXRpb24KCkRvZXMgZXN0aW1hdGVkIGdhbW1hIHNpZ25pZmljYW50bHkgaW5jcmVhc2UgYWZ0ZXIgb3Zlcm5pZ2h0IHJlc3Q/CgpgYGB7ciBzdGF0cy1nYW1tYS1pbmNyZWFzZX0KcGFyYW1zICU+JQogIGZpbHRlcigKICAgIHN0dWR5ICVpbiUgYygiU3R1ZHkgMiIsICJTdHVkeSAzIiksCiAgICBtZWFzdXJlbWVudF9pZCAlaW4lIGMoIkQxIiwgIkQyIiksCiAgICBtb2RlbCA9PSAic3IiLAogICAgcGFyYW1fbmFtZSA9PSAic3JfZ2FtbWEiCiAgKSAlPiUKICBzZWxlY3Qoc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNyX2dhbW1hID0gcGFyYW1fdmFsdWUpICU+JQogIGdyb3VwX2J5KG1lYXN1cmVtZW50X2lkKSAlPiUKICBzdW1tYXJpc2UobWVkaWFuX2dhbW1hID0gbWVkaWFuKHNyX2dhbW1hKSkgJT4lCiAga2JsKAogICAgY2FwdGlvbiA9IHN0cl9jKAogICAgICAiPGNlbnRlcj4iLCAiTWVkaWFuIFNSIGdhbW1hIGJlZm9yZS9hZnRlciBvdmVybmlnaHQgcmVzdCIsICI8L2NlbnRlcj4iCiAgICApLAogICAgZGlnaXRzID0gMwogICkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInJlc3BvbnNpdmUiKSkKCnBhcmFtcyAlPiUKICBmaWx0ZXIoCiAgICBzdHVkeSAlaW4lIGMoIlN0dWR5IDIiLCAiU3R1ZHkgMyIpLAogICAgbWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMiIpLAogICAgbW9kZWwgPT0gInNyIiwKICAgIHBhcmFtX25hbWUgPT0gInNyX2dhbW1hIgogICkgJT4lCiAgc2VsZWN0KHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkLCBzcl9nYW1tYSA9IHBhcmFtX3ZhbHVlKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbWVhc3VyZW1lbnRfaWQsIHZhbHVlc19mcm9tID0gc3JfZ2FtbWEpICU+JQogIHdpdGgoCiAgICB3aWxjb3gudGVzdCgKICAgICAgRDIsIEQxLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIiwgcGFpcmVkID0gVFJVRSwgY29uZi5pbnQgPSBUUlVFCiAgICApCiAgKSAlPiUKICB0aWR5KCkgJT4lCiAga2JsKAogICAgY2FwdGlvbiA9IHN0cl9jKAogICAgICAiPGNlbnRlcj4iLCAiSW5jcmVhc2UgaW4gU1IgZ2FtbWEgYWZ0ZXIgb3Zlcm5pZ2h0IHJlc3QiLCAiPC9jZW50ZXI+IgogICAgKSwKICAgIGRpZ2l0cyA9IDMKICApICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJyZXNwb25zaXZlIikpCmBgYAoKYGBge3IgcGxvdC1nYW1tYS1pbmNyZWFzZX0KcGxvdF9nYW1tYV9jaGFuZ2UgPC0gcGFyYW1zICU+JQogIGZpbHRlcigKICAgIHN0dWR5ICVpbiUgYygiU3R1ZHkgMiIsICJTdHVkeSAzIiksCiAgICBtZWFzdXJlbWVudF9pZCAlaW4lIGMoIkQxIiwgIkQyIiksCiAgICBtb2RlbCA9PSAic3IiLAogICAgcGFyYW1fbmFtZSA9PSAic3JfZ2FtbWEiCiAgKSAlPiUKICBzZWxlY3Qoc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNyX2dhbW1hID0gcGFyYW1fdmFsdWUpICU+JQogIGdncGxvdChhZXMoeD1tZWFzdXJlbWVudF9pZCwgeT1zcl9nYW1tYSwgY29sb3I9bWVhc3VyZW1lbnRfaWQpKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIGdlb21fcG9pbnQoCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKAogICAgICBqaXR0ZXIud2lkdGggPSAwLjI1LCBqaXR0ZXIuaGVpZ2h0ID0gMCwgZG9kZ2Uud2lkdGggPSAwLjUsIHNlZWQgPSAxCiAgICApLAogICAgYWxwaGEgPSAwLjI1LAogICAgc2hvdy5sZWdlbmQgPSBGQUxTRQogICkgKwogIHN0YXRfc3VtbWFyeSgKICAgIGdlb20gPSAiY3Jvc3NiYXIiLCBmdW4gPSBtZWRpYW4sIHBvc2l0aW9uID0gImRvZGdlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRQogICkgKwogIHNjYWxlX3hfZGlzY3JldGUoCiAgICBuYW1lID0gIk1lYXN1cmVtZW50IiwKICAgIGxhYmVscyA9IGMoIkQxIj0iQmVmb3JlIHJlc3QiLCAiRDIiPSJBZnRlciByZXN0IikKICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gc3RyX2MoIkVzdGltYXRlZCAiLCB1bmljb2RlX2dyZWVrWyJnYW1tYSJdKSwKICAgIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjI1KQogICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIG5hbWUgPSAiTWVhc3VyZW1lbnQiLAogICAgdmFsdWVzID0gYygiRDEiPSIjZmE5ZmI1IiwgIkQyIj0iIzdhMDE3NyIpLAogICAgbGFiZWxzID0gYygiRDEiPSJCZWZvcmUgcmVzdCIsICJEMiI9IkFmdGVyIHJlc3QiKQogICkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAxLjEpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZ3RpdGxlKAogICAgc3RyX2MoCiAgICAgIHVuaWNvZGVfZ3JlZWtbIkRlbHRhIl0sIHVuaWNvZGVfZ3JlZWtbImdhbW1hIl0sICIgYWZ0ZXIgb3Zlcm5pZ2h0IHJlc3QiCiAgICApCiAgKQoKcGxvdF9nYW1tYV9jaGFuZ2UKCmlmIChrbml0dGluZykgewogIGdnc2F2ZSgKICAgIGhlcmUoImZpZ3VyZXMiLCBzdHJfYygiZ2FtbWFfY2hhbmdlIiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9nYW1tYV9jaGFuZ2UsCiAgICB3aWR0aCA9IDgvMywgaGVpZ2h0ID0gMi41LAogICAgdW5pdHMgPSAiaW4iLCBkcGkgPSAzMDAsCiAgICBkZXZpY2UgPSBjYWlyb19wZGYKICApCn0KYGBgCgpBcmUgY2hhbmdlcyBpbiBlc3RpbWF0ZWQgZ2FtbWEgcmVsYXRlZCB0byBjaGFuZ2VzIGluIG5hdmlnYXRpb24gYmVoYXZpb3JzPwoKYGBge3Igc3RhdHMtZ2FtbWEtYWNjdXJhY3l9CmJpbmRfcm93cyhuYXZfc3R1ZHkyLCBuYXZfc3R1ZHkzKSAlPiUKICBmaWx0ZXIobWVhc3VyZW1lbnRfaWQgJWluJSBjKCJEMSIsICJEMiIpKSAlPiUKICBncm91cF9ieShzdHVkeSwgc3ViX2lkLCBtZWFzdXJlbWVudF9pZCwgc2hvcnRlc3RfcGF0aCkgJT4lCiAgc3VtbWFyaXNlKGFjY3VyYWN5ID0gbWVhbihjb3JyZWN0KSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG1lYXN1cmVtZW50X2lkLCB2YWx1ZXNfZnJvbSA9IGFjY3VyYWN5KSAlPiUKICBtdXRhdGUoZGVsdGFfYWNjdXJhY3kgPSBEMiAtIEQxKSAlPiUKICBzZWxlY3QoLWMoRDEsIEQyKSkgJT4lCiAgIyAKICBsZWZ0X2pvaW4oCiAgICBwYXJhbXMgJT4lCiAgICAgIGZpbHRlcigKICAgICAgICBzdHVkeSAlaW4lIGMoIlN0dWR5IDIiLCAiU3R1ZHkgMyIpLAogICAgICAgIG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiKSwKICAgICAgICBtb2RlbCA9PSAic3IiLAogICAgICAgIHBhcmFtX25hbWUgPT0gInNyX2dhbW1hIgogICAgICApICU+JQogICAgICBzZWxlY3Qoc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNyX2dhbW1hID0gcGFyYW1fdmFsdWUpICU+JQogICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbWVhc3VyZW1lbnRfaWQsIHZhbHVlc19mcm9tID0gc3JfZ2FtbWEpICU+JQogICAgICBtdXRhdGUoZGVsdGFfc3IgPSBEMiAtIEQxKSAlPiUKICAgICAgc2VsZWN0KC1jKEQxLCBEMikpLAogICAgYnkgPSBjKCJzdHVkeSIsICJzdWJfaWQiKQogICkgJT4lCiAgZ3JvdXBfYnkoc2hvcnRlc3RfcGF0aCkgJT4lCiAgbmVzdCgpICU+JQogIG11dGF0ZSgKICAgIHRlc3QgPSBtYXAoCiAgICAgIC54ID0gZGF0YSwKICAgICAgLmYgPSB+d2l0aCgKICAgICAgICAueCwKICAgICAgICBjb3IudGVzdCgKICAgICAgICAgIGRlbHRhX2FjY3VyYWN5LCBkZWx0YV9zciwKICAgICAgICAgIG1ldGhvZCA9ICJzcGVhcm1hbiIsIGV4YWN0ID0gRkFMU0UsIGFsdGVybmF0aXZlID0gImdyZWF0ZXIiCiAgICAgICAgKQogICAgICApICU+JSB0aWR5KCkKICAgICkKICApICU+JQogIHVubmVzdCh0ZXN0KSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KC1kYXRhKSAlPiUKICBrYmwoCiAgICBjYXB0aW9uID0gc3RyX2MoCiAgICAgICI8Y2VudGVyPiIsICLiiIYgQWNjdXJhY3kgfiDiiIYgU1IgZ2FtbWEgKGFmdGVyIG92ZXJuaWdodCByZXN0KSIsICI8L2NlbnRlcj4iCiAgICApLAogICAgZGlnaXRzID0gMwogICkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInJlc3BvbnNpdmUiKSkKYGBgCgpgYGB7ciBwbG90LWdhbW1hLWFjY3VyYWN5fQpwbG90X2dhbW1hX2FjY3VyYWN5X2NoYW5nZSA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiKSkgJT4lCiAgZ3JvdXBfYnkoc3R1ZHksIHN1Yl9pZCwgbWVhc3VyZW1lbnRfaWQsIHNob3J0ZXN0X3BhdGgpICU+JQogIHN1bW1hcmlzZShhY2N1cmFjeSA9IG1lYW4oY29ycmVjdCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtZWFzdXJlbWVudF9pZCwgdmFsdWVzX2Zyb20gPSBhY2N1cmFjeSkgJT4lCiAgbXV0YXRlKGRlbHRhX2FjY3VyYWN5ID0gRDIgLSBEMSkgJT4lCiAgc2VsZWN0KC1jKEQxLCBEMikpICU+JQogICMgCiAgbGVmdF9qb2luKAogICAgcGFyYW1zICU+JQogICAgICBmaWx0ZXIoCiAgICAgICAgc3R1ZHkgJWluJSBjKCJTdHVkeSAyIiwgIlN0dWR5IDMiKSwKICAgICAgICBtZWFzdXJlbWVudF9pZCAlaW4lIGMoIkQxIiwgIkQyIiksCiAgICAgICAgbW9kZWwgPT0gInNyIiwKICAgICAgICBwYXJhbV9uYW1lID09ICJzcl9nYW1tYSIKICAgICAgKSAlPiUKICAgICAgc2VsZWN0KHN0dWR5LCBzdWJfaWQsIG1lYXN1cmVtZW50X2lkLCBzcl9nYW1tYSA9IHBhcmFtX3ZhbHVlKSAlPiUKICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG1lYXN1cmVtZW50X2lkLCB2YWx1ZXNfZnJvbSA9IHNyX2dhbW1hKSAlPiUKICAgICAgbXV0YXRlKGRlbHRhX3NyID0gRDIgLSBEMSkgJT4lCiAgICAgIHNlbGVjdCgtYyhEMSwgRDIpKSwKICAgIGJ5ID0gYygic3R1ZHkiLCAic3ViX2lkIikKICApICU+JQogIGdncGxvdChhZXMoeD1kZWx0YV9zciwgeT1kZWx0YV9hY2N1cmFjeSwgY29sb3I9c2hvcnRlc3RfcGF0aCkpICsKICB0aGVtZV9jdXN0b20oKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGxpbmV3aWR0aCA9IDEuNSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gc3RyX2MoIkNoYW5nZSBpbiAiLCB1bmljb2RlX2dyZWVrWyJnYW1tYSJdKSkgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkNoYW5nZSBpbiBhY2N1cmFjeSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICBuYW1lID0gIlNob3J0ZXN0IHBhdGggZGlzdGFuY2UiLAogICAgdmFsdWVzID0gYygiIzg4Q0NFRSIsICIjQ0M2Njc3IiwgIiNERENDNzciKQogICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMSwgMS4xKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ2d0aXRsZSgKICAgIHN0cl9jKAogICAgICB1bmljb2RlX2dyZWVrWyJEZWx0YSJdLCAiTmF2aWdhdGlvbiB+ICIsCiAgICAgIHVuaWNvZGVfZ3JlZWtbIkRlbHRhIl0sIHVuaWNvZGVfZ3JlZWtbImdhbW1hIl0KICAgICkKICApCgpwbG90X2dhbW1hX2FjY3VyYWN5X2NoYW5nZQoKaWYgKGtuaXR0aW5nKSB7CiAgc3VwcHJlc3NXYXJuaW5ncygKICAgIGdnc2F2ZSgKICAgICAgaGVyZSgiZmlndXJlcyIsIHN0cl9jKCJnYW1tYV9hY2N1cmFjeV9jaGFuZ2UiLCAiLnBkZiIpKSwKICAgICAgcGxvdCA9IHBsb3RfZ2FtbWFfYWNjdXJhY3lfY2hhbmdlLAogICAgICB3aWR0aCA9IDgvMywgaGVpZ2h0ID0gMi41LAogICAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICAgKQogICkKICAKICBnZ3NhdmUoCiAgICBoZXJlKCJmaWd1cmVzIiwgc3RyX2MoImdhbW1hX2FjY3VyYWN5X2NoYW5nZV9jYWlybyIsICIucGRmIikpLAogICAgcGxvdCA9IHBsb3RfZ2FtbWFfYWNjdXJhY3lfY2hhbmdlLAogICAgd2lkdGggPSA4LzMsIGhlaWdodCA9IDIuNSwKICAgIHVuaXRzID0gImluIiwgZHBpID0gMzAwLAogICAgZGV2aWNlID0gY2Fpcm9fcGRmCiAgKQp9CmBgYAoKCiMgRXZpZGVuY2Ugb2YgY2FjaGVkIHJlcHJlc2VudGF0aW9uCgpIZXJlLCB3ZSdyZSB0cnlpbmcgdG8gZ2V0IGEgc2Vuc2UgZm9yIHdoYXQgaXMgYmVpbmcgY2FjaGVkLCBhbmQgdHJ5aW5nIHRvIHNlZSBpZiB0aGVyZSdzIGV2aWRlbmNlIHRoYXQgY2FjaGluZyAoYXMgb3Bwb3NlZCB0byBtb2RlbC1iYXNlZCBwbGFubmluZykgaXMgdGhlIHByaW1hcnkgZHJpdmVyIG9mIG5hdmlnYXRpb24gaW1wcm92ZW1lbnQgYWZ0ZXIgb3Zlcm5pZ2h0IHJlc3QuCgojIyBTaW11bGF0aW9uIHZpc3VhbGl6YXRpb24KCmBgYHtyIHBsb3QtbmV0d29yay13aXRoLXNyfQpidXR0ZXJmbHlfbGF5b3V0IDwtIGNyZWF0ZV9sYXlvdXQoZywgbGF5b3V0ID0gInN0cmVzcyIpCgpwbG90X25ldHdvcmtfd2l0aF9zciA8LSBzaW1fc3JfbWF0cml4X2FzeW1wdG90aWMgJT4lCiAgbXV0YXRlKAogICAgZnJvbV9zb3J0ZWQgPSBpZl9lbHNlKGZyb20gPCB0bywgZnJvbSwgdG8pLAogICAgdG9fc29ydGVkID0gaWZfZWxzZShmcm9tIDwgdG8sIHRvLCBmcm9tKQogICkgJT4lCiAgZ3JvdXBfYnkoZ2FtbWEsIGZyb20gPSBmcm9tX3NvcnRlZCwgdG8gPSB0b19zb3J0ZWQpICU+JQogIHN1bW1hcmlzZShzcl92YWx1ZSA9IG1lYW4oc3JfdmFsdWUpKSAlPiUKICBmaWx0ZXIocm91bmQoZ2FtbWEsIDEpICVpbiUgYygwLjEsIDAuNSwgMC45KSkgJT4lCiAgZmlsdGVyKHNyX3ZhbHVlID4gMC4wNSkgJT4lCiAgbGVmdF9qb2luKGFkamxpc3QpICU+JQogIGZpbHRlcihmcm9tIDwgdG8pICU+JQogIG11dGF0ZSgKICAgIGVkZ2UgPSBmYWN0b3IoZWRnZSksCiAgICBnYW1tYSA9IHN0cl9jKHVuaWNvZGVfZ3JlZWtbImdhbW1hIl0sICIgPSAiLCBnYW1tYSkKICApICU+JQogIHRibF9ncmFwaChlZGdlcyA9IC4sIGRpcmVjdGVkID0gRkFMU0UpICU+JQogIG11dGF0ZShuYW1lID0gcm93X251bWJlcigpKSAlPiUKICBnZ3JhcGgoIm1hbnVhbCIsIHg9YnV0dGVyZmx5X2xheW91dCR4LCB5PWJ1dHRlcmZseV9sYXlvdXQkeSkgKwogIHRoZW1lX25ldHdvcmsoKSArCiAgZmFjZXRfZWRnZXMofmdhbW1hLCBuY29sID0gMSkgKwogIGdlb21fZWRnZV9saW5rKGFlcyhhbHBoYSA9IHNyX3ZhbHVlLCBjb2xvciA9IGVkZ2UpKSArCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IG5hbWUpKSArCiAgc2NhbGVfZWRnZV9jb2xvcl9tYW51YWwoCiAgICBuYW1lID0gTlVMTCwKICAgIHZhbHVlcyA9IGMoIjAiPSJyZWQiLCAiMSI9ImJsYWNrIiksCiAgICBsYWJlbHMgPSBjKCIwIj0iSW5mZXJyZWQgY29ubmVjdGlvbnMiLCAiMSI9Ik9ic2VydmVkIGNvbm5lY3Rpb25zIikKICApICsKICBzY2FsZV9lZGdlX2FscGhhKG5hbWUgPSAicChUYXJnZXQgfCBTb3VyY2UpIikgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQuYm94ID0gInZlcnRpY2FsIiwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMykKICApICsKICBnZ3RpdGxlKCJDb2duaXRpdmUgbWFwcyBwcmVkaWN0ZWQgYnkgU1IiKQoKcGxvdF9uZXR3b3JrX3dpdGhfc3IKCmlmIChrbml0dGluZykgewogIHN1cHByZXNzV2FybmluZ3MoCiAgICBnZ3NhdmUoCiAgICAgIGhlcmUoImZpZ3VyZXMiLCBzdHJfYygibmV0d29ya193aXRoX3NyIiwgIi5wZGYiKSksCiAgICAgIHBsb3QgPSBwbG90X25ldHdvcmtfd2l0aF9zciwKICAgICAgd2lkdGggPSAzLjUsIGhlaWdodCA9IDYsCiAgICAgIHVuaXRzID0gImluIiwgZHBpID0gMzAwCiAgICApCiAgKQogIAogIGdnc2F2ZSgKICAgIGhlcmUoImZpZ3VyZXMiLCBzdHJfYygibmV0d29ya193aXRoX3NyX2NhaXJvIiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9uZXR3b3JrX3dpdGhfc3IsCiAgICB3aWR0aCA9IDMuNSwgaGVpZ2h0ID0gNiwKICAgIHVuaXRzID0gImluIiwgZHBpID0gMzAwLAogICAgZGV2aWNlID0gY2Fpcm9fcGRmCiAgKQp9CmBgYAoKIyMgVHJhbnNpdGlvbiByZWV2YWx1YXRpb24KCkZvbGxvd2luZyB0cmFuc2l0aW9uIHJlZXZhbHVhdGlvbiwgYSBjYWNoaW5nIGFjY291bnQgcHJlZGljdHMgdGhhdCBwZW9wbGUncyBuYXZpZ2F0aW9uIHNob3VsZCBnZXQgd29yc2UgcmVsYXRpdmUgdG8gdGhlaXIgcGVyZm9ybWFuY2UgYWZ0ZXIgb3Zlcm5pZ2h0IHJlc3QuIEluIGNvbnRyYXN0LCBhIHBsYW5uaW5nIGFjY291bnQgcHJlZGljdHMgdGhhdCBwZW9wbGUncyBuYXZpZ2F0aW9uIHNob3VsZCBub3QgYmUgZ3JlYXRseSBpbXBhY3RlZCBieSBhIHJlbGF0aXZlbHkgc21hbGwgc2V0IG9mIGNoYW5nZXMuCgpgYGB7ciBzdGF0cy1yZWV2YWx1YXRpb259CnN0YXRzX25hdl90cl9kaXN0MiA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiLCAiRDJiIikpICU+JQogIG11dGF0ZSgKICAgIG1lYXN1cmVtZW50X2lkID0gZmN0X3JlbGV2ZWwobWVhc3VyZW1lbnRfaWQsICJEMmIiKSwKICAgIHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMiIpCiAgKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSArICgxIHwgc3R1ZHkpLAogICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICBkYXRhID0gLgogICkKCnN0YXRzX25hdl90cl9kaXN0MyA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiLCAiRDJiIikpICU+JQogIG11dGF0ZSgKICAgIG1lYXN1cmVtZW50X2lkID0gZmN0X3JlbGV2ZWwobWVhc3VyZW1lbnRfaWQsICJEMmIiKSwKICAgIHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiMyIpCiAgKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSArICgxIHwgc3R1ZHkpLAogICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICBkYXRhID0gLgogICkKCnN0YXRzX25hdl90cl9kaXN0NCA8LSBiaW5kX3Jvd3MobmF2X3N0dWR5MiwgbmF2X3N0dWR5MykgJT4lCiAgZmlsdGVyKG1lYXN1cmVtZW50X2lkICVpbiUgYygiRDEiLCAiRDIiLCAiRDJiIikpICU+JQogIG11dGF0ZSgKICAgIG1lYXN1cmVtZW50X2lkID0gZmN0X3JlbGV2ZWwobWVhc3VyZW1lbnRfaWQsICJEMmIiKSwKICAgIHNob3J0ZXN0X3BhdGggPSBmY3RfcmVsZXZlbChzaG9ydGVzdF9wYXRoLCAiNCIpCiAgKSAlPiUKICBnbG1tVE1CKAogICAgY29ycmVjdCB+IHNob3J0ZXN0X3BhdGggKiBtZWFzdXJlbWVudF9pZCArCiAgICAgICgxICsgc2hvcnRlc3RfcGF0aCArIG1lYXN1cmVtZW50X2lkIHwgc3ViX2lkKSArICgxIHwgc3R1ZHkpLAogICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICBkYXRhID0gLgogICkKCm1hcF9kZnIoCiAgLnggPSBsaXN0KAogICAgImRpc3QtMiIgPSBzdGF0c19uYXZfdHJfZGlzdDIsCiAgICAiZGlzdC0zIiA9IHN0YXRzX25hdl90cl9kaXN0MywKICAgICJkaXN0LTQiID0gc3RhdHNfbmF2X3RyX2Rpc3Q0CiAgKSwKICAuZiA9IH50aWR5KC54LCBjb25mLmludCA9IFRSVUUpLAogIC5pZCA9ICJyZWZfY2F0IgopICU+JQogIGNoZWNrX3NpZ25pZmljYW5jZSgpICU+JQogIHNlbGVjdCgtYyhyZWZfY2F0LCBlZmZlY3QsIGNvbXBvbmVudCkpICU+JQogIGtibCgKICAgIGNhcHRpb24gPSBzdHJfYygKICAgICAgIjxjZW50ZXI+IiwgIk5hdmlnYXRpb24gYWNjdXJhY3k6IFRyYW5zaXRpb24gUmVldmFsdWF0aW9uIiwgIjwvY2VudGVyPiIKICAgICksCiAgICBkaWdpdHMgPSAzCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygicmVzcG9uc2l2ZSIpKSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTIiLCAxLCAyNSkgJT4lCiAgcGFja19yb3dzKCJSZWYuIENhdC4gZGlzdC0zIiwgMjYsIDUwKSAlPiUKICBwYWNrX3Jvd3MoIlJlZi4gQ2F0LiBkaXN0LTQiLCA1MSwgNzUpCmBgYAoKYGBge3IgcGxvdC10cn0KcHJlZGljdF90ciA8LSBleHBhbmRfZ3JpZCgKICBtZWFzdXJlbWVudF9pZCA9IGMoIkQxIiwgIkQyIiwgIkQyYiIpLAogIHNob3J0ZXN0X3BhdGggPSBmYWN0b3IoMjo0KSwKICBzdWJfaWQgPSBOQSwgc3R1ZHkgPSBOQQopICU+JQogIHByZWRpY3RfZ2xtbVRNQihzdGF0c19uYXZfdHJfZGlzdDIpCgpwbG90X25hdl90ciA8LSBuYXZfc3R1ZHkzICU+JQogIGZpbHRlcihtZWFzdXJlbWVudF9pZCAlaW4lIGMoIkQxIiwgIkQyIiwgIkQyYiIpKSAlPiUKICBncm91cF9ieShzdWJfaWQsIG1lYXN1cmVtZW50X2lkLCBzaG9ydGVzdF9wYXRoKSAlPiUKICBzdW1tYXJpc2UoYWNjdXJhY3kgPSBtZWFuKGNvcnJlY3QpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c2hvcnRlc3RfcGF0aCwgeT1hY2N1cmFjeSwgY29sb3I9bWVhc3VyZW1lbnRfaWQpKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fZG90cGxvdCgKICAgIGFlcyhmaWxsID0gbWVhc3VyZW1lbnRfaWQpLAogICAgYmlud2lkdGggPSAwLjAxLAogICAgYmluYXhpcyA9ICJ5Iiwgc3RhY2tkaXIgPSAiY2VudGVyIiwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjc1KSwKICAgIGRvdHNpemUgPSAxLCBhbHBoYSA9IDAuNSwgY29sb3IgPSBOQSwKICAgIHNob3cubGVnZW5kID0gRkFMU0UKICApICsKICBnZW9tX3BvaW50cmFuZ2UoCiAgICBhZXMoCiAgICAgIHggPSBzaG9ydGVzdF9wYXRoLCB5ID0gZml0LAogICAgICB5bWluID0gZml0IC0gc2UuZml0LCB5bWF4ID0gZml0ICsgc2UuZml0LAogICAgICBjb2xvciA9IG1lYXN1cmVtZW50X2lkCiAgICApLAogICAgZGF0YSA9IHByZWRpY3RfdHIsIGluaGVyaXQuYWVzID0gRkFMU0UsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4yNSksIGxpbmV3aWR0aCA9IDEKICApICsKICBnZW9tX2xpbmUoCiAgICBhZXMoCiAgICAgIHggPSBzaG9ydGVzdF9wYXRoLCB5ID0gZml0LAogICAgICBncm91cCA9IG1lYXN1cmVtZW50X2lkLCBjb2xvciA9IG1lYXN1cmVtZW50X2lkCiAgICApLAogICAgZGF0YSA9IHByZWRpY3RfdHIsIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4yNSksIGxpbmV3aWR0aCA9IDEKICApICsKICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiU2hvcnRlc3QgcGF0aCBkaXN0YW5jZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIkFjY3VyYWN5IiwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBicmVha3MgPSBzZXEoMCwgMSwgMC4yNSkKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICBuYW1lID0gIk1lYXN1cmVtZW50IiwKICAgIHZhbHVlcyA9IGMoIkQxIj0iI2ZhOWZiNSIsICJEMiI9IiM3YTAxNzciLCAiRDJiIj0iIzIzOGI0NSIpLAogICAgbGFiZWxzID0gYygKICAgICAgIkQxIj0iQmVmb3JlIG92ZXJuaWdodCByZXN0IiwKICAgICAgIkQyIj0iQWZ0ZXIgb3Zlcm5pZ2h0IHJlc3QiLAogICAgICAiRDJiIj0iQWZ0ZXIgcmVldmFsdWF0aW9uIgogICAgKQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgdmFsdWVzID0gYygiRDEiPSIjZmE5ZmI1IiwgIkQyIj0iIzdhMDE3NyIsICJEMmIiPSIjMjM4YjQ1IikKICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMC4zLCAxLjEpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZ3RpdGxlKCJOYXZpZ2F0aW9uIGFmdGVyIHRyYW5zaXRpb24gcmVldmFsdWF0aW9uIikKCnBsb3RfbmF2X3RyCgppZiAoa25pdHRpbmcpIHsKICBnZ3NhdmUoCiAgICBoZXJlKCJmaWd1cmVzIiwgc3RyX2MoIm5hdmlnYXRpb25fcmVldmFsdWF0aW9uIiwgIi5wZGYiKSksCiAgICBwbG90ID0gcGxvdF9uYXZfdHIsCiAgICB3aWR0aCA9IDQsIGhlaWdodCA9IDQsCiAgICB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMAogICkKfQpgYGAKCgojIFNlc3Npb24gaW5mbwoKRm9yIHJlcHJvZHVjaWJpbGl0eS4KCmBgYHtyIHNlc3Npb24taW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAKCg==